diff --git a/src/main/java/lang/jimple/util/Pair.java b/src/main/java/lang/jimple/util/Pair.java index 86ea2818..04df096a 100644 --- a/src/main/java/lang/jimple/util/Pair.java +++ b/src/main/java/lang/jimple/util/Pair.java @@ -1,5 +1,8 @@ package lang.jimple.util; +import lang.jimple.internal.generated.Type; +import lang.jimple.internal.generated.Value; + import lombok.AllArgsConstructor; import lombok.Data; @@ -9,5 +12,4 @@ public class Pair { First first; Second second; - } diff --git a/src/main/rascal/lang/jimple/core/Syntax.rsc b/src/main/rascal/lang/jimple/core/Syntax.rsc index b2979589..a68bdfaf 100644 --- a/src/main/rascal/lang/jimple/core/Syntax.rsc +++ b/src/main/rascal/lang/jimple/core/Syntax.rsc @@ -135,6 +135,7 @@ data Expression | lengthOf(Immediate immediate) | neg(Immediate immediate) | immediate(Immediate immediate) + | phiFunction(Variable variable, list[Variable] definitions) ; data ArrayDescriptor diff --git a/src/main/rascal/lang/jimple/decompiler/Decompiler.rsc b/src/main/rascal/lang/jimple/decompiler/Decompiler.rsc index e38b0d31..6f7d8ef2 100644 --- a/src/main/rascal/lang/jimple/decompiler/Decompiler.rsc +++ b/src/main/rascal/lang/jimple/decompiler/Decompiler.rsc @@ -8,7 +8,7 @@ import Exception; @javaClass{lang.jimple.internal.Decompiler} @reflect{for stdout} -java ClassOrInterfaceDeclaration decompile(loc classFile) throws IO; +java ClassOrInterfaceDeclaration decompile(loc classFile) throws IO; diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/DominanceFrontier.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/DominanceFrontier.rsc new file mode 100644 index 00000000..3c52d08d --- /dev/null +++ b/src/main/rascal/lang/jimple/toolkit/ssa/DominanceFrontier.rsc @@ -0,0 +1,24 @@ +module lang::jimple::toolkit::ssa::DominanceFrontier + +import analysis::graphs::Graph; +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::toolkit::ssa::DominanceTree; + +public map[Node, set[Node]] createDominanceFrontier(FlowGraph flowGraph, map[&T, set[&T]] dominanceTree) { + allNodes = { origin | <- flowGraph } + { destination | <_, destination> <- flowGraph }; + + dominanceFrontiers = (); + + for(graphNode <- allNodes) { + for(predecessor <- predecessors(flowGraph, graphNode)) { + tempPredecessor = predecessor; + while(tempPredecessor != findIdom(dominanceTree, graphNode)) { + dominanceFrontiers[tempPredecessor] = tempPredecessor in dominanceFrontiers ? dominanceFrontiers[tempPredecessor] : {}; + dominanceFrontiers[tempPredecessor] = dominanceFrontiers[tempPredecessor] + {graphNode}; + tempPredecessor = findIdom(dominanceTree, tempPredecessor); + }; + }; + }; + + return dominanceFrontiers; +} diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/DominanceTree.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/DominanceTree.rsc index 65d90836..10ad991b 100644 --- a/src/main/rascal/lang/jimple/toolkit/ssa/DominanceTree.rsc +++ b/src/main/rascal/lang/jimple/toolkit/ssa/DominanceTree.rsc @@ -1,16 +1,54 @@ module lang::jimple::toolkit::ssa::DominanceTree +import demo::Dominators; +import Set; import lang::jimple::toolkit::FlowGraph; +import analysis::graphs::Graph; +import demo::Dominators; -import List; +public map[&T, set[&T]] createDominanceTree(Graph[&T] graph) { + rel[&T, set[&T]] dominanceTreeRelations = dominators(graph, entryNode()); + return ( father: childs | <- dominanceTreeRelations ); +} + +public map[Node, list[Node]] createAdjacenciesMatrix(FlowGraph flowGraph) { + blockTree = ( origin: [] | <- flowGraph); + + for( <- flowGraph) { + blockTree[origin] = blockTree[origin] + [destination]; + }; + + return blockTree; +} + +public map[Node, list[Node]] createIdomTree(map[&T, set[&T]] dominanceTree) { + blockTree = ( treeKey: [] | treeKey <- dominanceTree); + + for(treeKey <- dominanceTree) { + idom = findIdom(dominanceTree, treeKey); + + if(treeKey != entryNode()) + blockTree[idom] = blockTree[idom] + [treeKey]; + }; -public map[Node, Node] createDominanceTree(FlowGraph flowGraph) { - result = (computeDominator(flowGraph, a): a | <- flowGraph); - return result; + return blockTree; } -public Node computeDominator(FlowGraph flowGraph, Node currentNode) { - predecessorNodeList = [a | <- flowGraph, b == currentNode]; - result = if(isEmpty(predecessorNodeList) == true) currentNode; else head(predecessorNodeList); - return result; -} \ No newline at end of file +public Graph[&T] removeNodeFromGraph(Graph[&T] graph, &T nodeToRemove) { + return { | <- graph, nodeToRemove != father || nodeToRemove == entryNode() }; +} + + +public Node findIdom(map[&T, set[&T]] dominanceTree, Node child) { + idom = entryNode(); + + possibleIdoms = [ father | father <- dominanceTree, child in dominanceTree[father] ]; + + for(possibleIdom <- possibleIdoms) { + if(size(dominanceTree[possibleIdom]) < size(dominanceTree[idom])) { + idom = possibleIdom; + }; + }; + + return idom; +} diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/Generator.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/Generator.rsc new file mode 100644 index 00000000..73799379 --- /dev/null +++ b/src/main/rascal/lang/jimple/toolkit/ssa/Generator.rsc @@ -0,0 +1,39 @@ +module lang::jimple::toolkit::ssa::Generator + +import List; + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; + +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::toolkit::ssa::PhiFunctionInsertion; +import lang::jimple::toolkit::ssa::DominanceFrontier; +import lang::jimple::toolkit::ssa::VariableRenaming; + +public FlowGraph applySSATransformation(MethodBody methodBody) { + FlowGraph flowGraph = forwardFlowGraph(methodBody); + + if(hasUnsupportedInstruction(flowGraph)) // Doest not support invokeStmt and fieldRef rename + return {}; + + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result; +} + +public bool hasUnsupportedInstruction(FlowGraph flowGraph) { + unsupportedList = [ | <- flowGraph, unspportedStatement(origin) || unspportedStatement(destination)]; + + return size(unsupportedList) != 0; +} + +public bool unspportedStatement(Node nodeStatement) { + switch(nodeStatement) { + case stmtNode(invokeStmt(_)): return true; + case stmtNode(assign(fieldRef(_, _), _)): return true; + default: return false; + } +} \ No newline at end of file diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/Helpers.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/Helpers.rsc new file mode 100644 index 00000000..f552e9bb --- /dev/null +++ b/src/main/rascal/lang/jimple/toolkit/ssa/Helpers.rsc @@ -0,0 +1,115 @@ +module lang::jimple::toolkit::ssa::Helpers + +import Node; +import Type; +import List; + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; + +public Variable returnStmtVariable(Node graphNode) { + assignStatement = returnStmtNodeBody(graphNode); + variableArg = assignStatement[0]; + + switch(variableArg) { + case Variable variable: return variable; + } +} + +public Statement returnStmtNodeBody(Node stmtNode) { + switch(stmtNode) { + case stmtNode(stmtBody): return stmtBody; + } +} + +public Variable returnPhiFunctionLeftHandSide(Expression phiFunctionExpr) { + switch(phiFunctionExpr) { + case phiFunction(leftHandSide, _): return leftHandSide; + } +} + +public list[Variable] returnPhiFunctionRightHandSide(Expression phiFunctionExpr) { + switch(phiFunctionExpr) { + case phiFunction(_, rightHandSide): return rightHandSide; + } +} + +public Variable returnLeftHandSideVariable(Node stmtNode) { + switch(stmtNode) { + case stmtNode(assign(leftHandSide, _)): return leftHandSide; + } +} + +public Expression returnRightHandSideExpression(Node stmtNode) { + switch(stmtNode) { + case stmtNode(assign(_, rightHandSide)): return rightHandSide; + } +} + +public String returnVariableImmediateName(Immediate immediate) { + switch(immediate) { + case local(String localName): return localName; + default: return ""; + } +} + +public String returnImmediateName(Immediate immediate) { + switch(immediate[0]) { + case String immediateName: return immediateName; + } +} + +public list[Immediate] returnRightHandSideImmediates(Node variableNode) { + rightHandSide = returnRightHandSideExpression(variableNode); + typeOfVariableArg = typeOf(rightHandSide); + + if(typeOfVariableArg.name != "Expression") return []; + + list[Immediate] immediates = returnExpressionImmediates(rightHandSide); + list[Immediate] localVariableImmediates = [ immediate | immediate <- immediates, returnVariableImmediateName(immediate) != ""]; + int variablesCount = size(localVariableImmediates); + + if(variablesCount != 0) return localVariableImmediates; + + return []; +} + +public list[Immediate] returnExpressionImmediates(Expression expression) { + switch(expression) { + case newInstance(Type instanceType): return []; + case newArray(Type baseType, list[ArrayDescriptor] dims): return []; + case cast(Type toType, local(name)): return []; + case instanceOf(Type baseType, local(name)): return [local(name)]; + case invokeExp(_): return []; + case arraySubscript(Name arrayName, _): return [local(arrayName)]; + case stringSubscript(String stringName, local(name)): return [local(stringName)]; + case localFieldRef(Name local, Name className, Type fieldType, Name fieldName): return []; + case fieldRef(Name className, Type fieldType, Name fieldName): return []; + case and(lhs, rhs): return [lhs, rhs]; + case or(lhs, rhs): return [lhs, rhs]; + case xor(lhs, rhs): return [lhs, rhs]; + case reminder(lhs, rhs): return [lhs, rhs]; + case isNull(variable): return [variable]; + case isNotNull(variable): return [variable]; + case cmp(lhs, rhs): return [lhs, rhs]; + case cmpg(lhs, rhs): return [lhs, rhs]; + case cmpl(lhs, rhs): return [lhs, rhs]; + case cmpeq(lhs, rhs): return [lhs, rhs]; + case cmpne(lhs, rhs): return [lhs, rhs]; + case cmpgt(lhs, rhs): return [lhs, rhs]; + case cmpge(lhs, rhs): return [lhs, rhs]; + case cmplt(lhs, rhs): return [lhs, rhs]; + case cmple(lhs, rhs): return [lhs, rhs]; + case shl(lhs, rhs): return [lhs, rhs]; + case shr(lhs, rhs): return [lhs, rhs]; + case ushr(lhs, rhs): return [lhs, rhs]; + case plus(lhs, rhs): return [lhs, rhs]; + case minus(lhs, rhs): return [lhs, rhs]; + case mult(lhs, rhs): return [lhs, rhs]; + case div(lhs, rhs): return [lhs, rhs]; + case lengthOf(local(name)): return [local(name)]; + case neg(local(name)): return [local(name)]; + case immediate(local(name)): return [local(name)]; + default: return []; + } +} diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/PhiFunctionInsertion.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/PhiFunctionInsertion.rsc new file mode 100644 index 00000000..1f7b2fd3 --- /dev/null +++ b/src/main/rascal/lang/jimple/toolkit/ssa/PhiFunctionInsertion.rsc @@ -0,0 +1,103 @@ +module lang::jimple::toolkit::ssa::PhiFunctionInsertion + +import demo::Dominators; +import Set; +import Relation; +import Type; +import IO; +import Node; +import List; + +import analysis::graphs::Graph; +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; +import lang::jimple::toolkit::ssa::Helpers; + +public FlowGraph insertPhiFunctions(FlowGraph flowGraph, map[&T, set[&T]] dominanceFrontier) { + variableList = { getStmtVariable(graphNode) | <- flowGraph, isVariable(graphNode) }; + + for(V <- variableList) { + DomFromPlus = (); + Work = (); + W = {}; + + for(X <- blocksWithVariable(flowGraph, V)) { + Work[X] = 1; + W = W + {X}; + }; + + while(W != {}) { + = takeOneFrom(W); + W = newSet; + + if(X in dominanceFrontier) { + for(Y <- dominanceFrontier[X]) { + if(!(Y in DomFromPlus)) { + flowGraph = insertPhiFunction(flowGraph, Y, V); // add v←φ(...) at entry of Y + DomFromPlus[Y] = 1; + if(!(Y in DomFromPlus)) { + Work(Y) = 1; + W = W + {Y}; + }; + }; + }; + }; + }; + }; + + return flowGraph; + +} + +public list[Node] blocksWithVariable(FlowGraph flowGraph, Variable variable) { + return [graphNode | <- flowGraph, isSameVariable(graphNode, variable)];; +} + +public bool isVariable(Node graphNode) { + if (size(graphNode[..]) == 0) return false; + + assignStatement = returnStmtNodeBody(graphNode); + if (size(assignStatement[..]) == 0) return false; + + variableArg = assignStatement[0]; + typeOfVariableArg = typeOf(variableArg); + + if (size(typeOfVariableArg[..]) == 0) return false; + + return typeOfVariableArg.name == "Variable"; +} + +public &T getStmtVariable(Node graphNode) { + assignStatement = returnStmtNodeBody(graphNode); + variableArg = assignStatement[0]; + + return variableArg; +} + +public bool isSameVariable(Node graphNode, Variable variable) { + if (size(graphNode[..]) == 0) return false; + + assignStatement = returnStmtNodeBody(graphNode); + if (size(assignStatement[..]) == 0) return false; + + variableArg = assignStatement[0]; + typeOfVariableArg = typeOf(variableArg); + + if (size(typeOfVariableArg[..]) == 0) return false; + + return variableArg == variable; +} + +public FlowGraph insertPhiFunction(FlowGraph flowGraph, Node childNode, Variable variable) { + if (childNode == exitNode()) return flowGraph; + + fatherNodes = predecessors(flowGraph, childNode); + phiFunctionStmt = stmtNode(assign(variable, phiFunction(variable, []))); + + phiFunctionRelations = { | fatherNode <- fatherNodes }; + filteredFlowGraph = { | <- flowGraph, !(origin in fatherNodes) || !(childNode == destination) }; + + flowGraphWithPhiFunction = phiFunctionRelations + filteredFlowGraph + {}; + + return flowGraphWithPhiFunction; +} diff --git a/src/main/rascal/lang/jimple/toolkit/ssa/VariableRenaming.rsc b/src/main/rascal/lang/jimple/toolkit/ssa/VariableRenaming.rsc new file mode 100644 index 00000000..910c2eda --- /dev/null +++ b/src/main/rascal/lang/jimple/toolkit/ssa/VariableRenaming.rsc @@ -0,0 +1,408 @@ +module lang::jimple::toolkit::ssa::VariableRenaming + +import Set; +import Map; +import Relation; +import Node; +import List; +import Type; +import String; +import util::Math; + +import analysis::graphs::Graph; + +import lang::jimple::util::Stack; +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::toolkit::ssa::Helpers; + +map[str, Stack[int]] VARIABLE_VERSION_STACK = (); +map[str, int] VARIABLE_ASSIGNMENT_COUNT = (); +set[Node] REPLACED_NODES = {}; +map[Node, Node] LAST_VERSION_REPLACED = (); +map[Node, list[Node]] IDOM_TREE; +map[Node, list[Node]] ADJACENCIES_MATRIX; + +public FlowGraph applyVariableRenaming(FlowGraph flowGraph) { + VARIABLE_VERSION_STACK = (); // Stack for each variable that holds the next variable to ve replaced + VARIABLE_ASSIGNMENT_COUNT = (); // Counts how many assingments have been processed for a given variable + REPLACED_NODES = {}; // Keep tracking of all nodes replaced in one execution + LAST_VERSION_REPLACED = (); // Keep tracking of the new version of a given node + + ADJACENCIES_MATRIX = createAdjacenciesMatrix(flowGraph); // Mainly used to rebuild the renamed flow graph + IDOM_TREE = createIdomTree(createDominanceTree(flowGraph)); // Used to traverse the flow graph + + map[Node, list[Node]] newBlockTree = replace(entryNode()); // Start algorithm + + // Rebuild renamed flowGraph + FlowGraph newFlowGraph = {}; + for(fatherNode <- newBlockTree) { + newFlowGraph = newFlowGraph + { | nodeChild <- ADJACENCIES_MATRIX[fatherNode]}; + }; + + return newFlowGraph; +} + +public map[Node, list[Node]] replace(Node X) { + if((X == exitNode())) return ADJACENCIES_MATRIX; + + Node oldNode = X; + + // Deal with all nodes that aren't assigments bug uses a variable in some way + if(isNonAssignmentStatementToRename(X)) { + statementBody = returnStmtNodeBody(X); + Node renamedStatement = stmtNode(replaceImmediateUse(statementBody)); + renameNodeOcurrecies(X, renamedStatement); + X = renamedStatement; + }; + + // Deal will all nodes that are an assigment and are not renamed + if(isOrdinaryAssignment(X) && !isRenamed(X)) { + + // Replace right hand side variables + list[Immediate] rightHandNodeImmediates = returnRightHandSideImmediates(X); + for(rightHandSideImmediate <- rightHandNodeImmediates) { + newAssignStmt = replaceRightVariableVersion(ADJACENCIES_MATRIX, rightHandSideImmediate, X); + + renameNodeOcurrecies(X, newAssignStmt); + + X = newAssignStmt; + }; + + // Replace left hand side variables + if(isLeftHandSideVariable(X)) { + Variable V = returnStmtVariable(X); + Immediate localVariableImmediate = local(V[0]); + + int assingmentQuantity = returnAssignmentQuantity(localVariableImmediate); + newAssignStmt = replaceLeftVariableVersion(ADJACENCIES_MATRIX, X, assingmentQuantity); + + renameNodeOcurrecies(X, newAssignStmt); + + stackVariableVersion(localVariableImmediate, assingmentQuantity); + iterateAssignmentQuantity(localVariableImmediate); + findAndAddPhiFunctionArgs(oldNode, newAssignStmt); // Search variable uses of replaced variable in phi-function and rename it + + X = newAssignStmt; + }; + } + + for(child <- IDOM_TREE[X]) { + // We need to check the last version replaced because the renamed version was not beign reflected in the current `for` statement iteration, + // so we need to check the last version replaced + nodeToRename = LAST_VERSION_REPLACED[child]? ? LAST_VERSION_REPLACED[child] : child; + replace(nodeToRename); + }; + + if(!ignoreNode(oldNode) && isVariable(oldNode) && !isRenamed(X)) + popOldNode(oldNode); + + return ADJACENCIES_MATRIX; +} + +public void findAndAddPhiFunctionArgs(Node oldNode, Node newNode) { + Variable variable = returnStmtVariable(oldNode); + variableName = variable[0]; + dfsPhiFunctionLookupAndRename(newNode, newNode, variableName); +} + +public void dfsPhiFunctionLookupAndRename(Node originalNode, Node entryNode, str variableName) { + if(!(entryNode in ADJACENCIES_MATRIX)) return; + if(size(ADJACENCIES_MATRIX[entryNode]) != 1) return; + + for(child <- ADJACENCIES_MATRIX[entryNode]) { + if(child == originalNode) return; + + if(matchPhiFunction(child, variableName)) { + + oldPhiFunctionStmt = child; + newPhiFunctionStmt = replacePhiFunctionVersion(ADJACENCIES_MATRIX, child); + renameNodeOcurrecies(oldPhiFunctionStmt, newPhiFunctionStmt); + + return; + }; + + dfsPhiFunctionLookupAndRename(originalNode, child, variableName); + }; + + return; +} + +public bool matchPhiFunction(Node variableNode, str variableName) { + switch(variableNode){ + case stmtNode(assign(_, phiFunction(localVariable(name), _))): return name == variableName; + case stmtNode(assign(_, phiFunction(arrayRef(name, _), _))): return name == variableName; + default: return false; + }; +} + +public void renameNodeOcurrecies(Node oldStmt, Node newStmt) { + ADJACENCIES_MATRIX = replaceNodeOcurrenciesInTrees(ADJACENCIES_MATRIX, oldStmt, newStmt); + IDOM_TREE = replaceNodeOcurrenciesInTrees(IDOM_TREE, oldStmt, newStmt); + LAST_VERSION_REPLACED[oldStmt] = newStmt; +} + +public map[Node, list[Node]] replaceNodeOcurrenciesInTrees(map[Node, list[Node]] blockTree, Node oldNode, Node newRenamedNode) { + for(key <- blockTree) { + if(oldNode in blockTree[key]) { + blockTree[key] = blockTree[key] - [oldNode] + [newRenamedNode]; + }; + }; + + blockTree[newRenamedNode] = blockTree[oldNode]; + blockTree = delete(blockTree, oldNode); + REPLACED_NODES = REPLACED_NODES + {newRenamedNode}; + + return blockTree; +} + +public Statement replaceImmediateUse(Statement statement) { + return addEnclosingStmt(statement, replaceImmediateUse(statement[0])); +} + +public Statement addEnclosingStmt(Statement statement, Expression expression) { + statement[0] = expression; + return statement; +} + +public Statement addEnclosingStmt(Statement statement, Immediate immediate) { + statement[0] = immediate; + return statement; +} + +public Immediate replaceImmediateUse(Immediate immediate) { + return local(returnLastVariableVersion(immediate[0])); +} + +public Expression replaceImmediateUse(Expression expression) { + int i = 0; + + for(stmtArgument <- expression) { + if(isSkipableStatement(stmtArgument)) { + expression[i] = stmtArgument; + } else { + expression[i] = replaceImmediateUse(stmtArgument); + }; + + i = i + 1; + }; + + return expression; +} + +public Name returnLastVariableVersion(str name) { + Immediate immediate = local(name); + variableName = returnVariableImmediateName(immediate); + + int versionIndex = getVariableVersionStacked(immediate); + str newVariableName = buildVersionName(variableName, versionIndex); + + return newVariableName; +} + +public bool isReplaced(Node stmtNode) { + return stmtNode in REPLACED_NODES; +} + +public bool isSkipableStatement(stmtArgument) { + switch(stmtArgument) { + case str stringArgument: return true; + case gotoStmt(_): return true; + case iValue(_): return true; + case caughtException(): return true; + }; + + return false; +} + +public Node replacePhiFunctionVersion(map[Node, list[Node]] blockTree, Node variableNode) { + assignVariable = returnLeftHandSideVariable(variableNode); + assignPhiFunction = returnRightHandSideExpression(variableNode); + + phiFunctionVariable = returnPhiFunctionLeftHandSide(assignPhiFunction); + variableVersionList = returnPhiFunctionRightHandSide(assignPhiFunction); + + variableName = phiFunctionVariable[0]; + Immediate localVariableImmediate = local(variableName); + versionIndex = getVariableVersionStacked(localVariableImmediate); + + str newVariableName = buildVersionName(variableName, versionIndex); + + list[Variable] newVariableList = variableVersionList + [localVariable(newVariableName)]; + Node renamedPhiFunction = stmtNode(assign(assignVariable, phiFunction(phiFunctionVariable, newVariableList))); + + return renamedPhiFunction; +} + +public Node replaceLeftVariableVersion(map[Node, list[Node]] blockTree, Node variableNode, int versionIndex) { + switch(variableNode) { + case stmtNode(assign(localVariable(localName), rhs)): + return stmtNode(assign(localVariable(buildVersionName(localName, versionIndex)), rhs)); + case stmtNode(assign(arrayRef(arrayName, local(localName)), rhs)): + return stmtNode(assign(arrayRef(buildVersionName(arrayName, versionIndex), local(returnCurrentVersionName(local(localName)))) , rhs)); + case stmtNode(assign(arrayRef(arrayName, immediate), rhs)): + return stmtNode(assign(arrayRef(buildVersionName(arrayName, versionIndex), immediate), rhs)); + }; +} + +public Node replaceRightVariableVersion(map[Node, list[Node]] blockTree, Immediate immediateToRename, Node variableNode) { + leftHandSide = returnLeftHandSideVariable(variableNode); + Expression rightHandSide = returnRightHandSideExpression(variableNode); + rightHandSide = renameExpressionVariables(rightHandSide, immediateToRename); + return stmtNode(assign(leftHandSide, rightHandSide)); +} + +public Expression renameExpressionVariables(Expression expression, Immediate immediateToRename) { + String newVersionName = returnCurrentVersionName(immediateToRename); + + switch(expression) { + case arraySubscript(arrayName, local(localName)): + return arraySubscript(newVersionName, local(returnCurrentVersionName(local(localName)))); + }; + + list[Immediate] immediates = returnExpressionImmediates(expression); + int index = indexOf(immediates, immediateToRename); + expression[index] = local(newVersionName); + + return expression; +} + +public String returnCurrentVersionName(Immediate immediate) { + int variableVersion = getVariableVersionStacked(immediate); + String variableOriginalName = returnImmediateName(immediate); + return buildVersionName(variableOriginalName, variableVersion); +} + +public bool isRenamed(Node assignNode) { + if(ignoreNode(assignNode)) return false; + + assignStmt = returnStmtNodeBody(assignNode); + + switch(assignNode) { + case stmtNode(assign(localVariable(name), _)) : return contains(name, "version"); + case stmtNode(assign(arrayRef(arrayName, _), _)): return contains(arrayName, "version"); + } +} + +public str buildVersionName(str variableOriginalName, int versionIndex) { + return variableOriginalName + "_version-" + toString(versionIndex); +} + +public bool isLeftHandSideVariable(Node variableNode) { + switch(variableNode) { + case stmtNode(assign(localVariable(_), _)): return true; + case stmtNode(assign(arrayRef(_, _), _)): return true; + default: return false; + } +} + +public bool ignoreNode(Node variableNode) { + switch(variableNode) { + case entryNode(): return true; + case skipNode(): return true; + case exitNode(): return true; + case stmtNode(gotoStmt(_)): return true; + case stmtNode(identity(_, _, _)): return true; + case stmtNode(returnEmptyStmt()): return true; + case stmtNode(returnStmt(iValue(_))): return true; + default: return false; + } +} + +public bool isOrdinaryAssignment(Node variableNode) { + if(ignoreNode(variableNode)) return false; + + statement = returnStmtNodeBody(variableNode); + switch(statement) { + case assign(_, _): return true; + default: return false; + } +} + +public bool isPhiFunctionAssigment(Node variableNode) { + if(ignoreNode(variableNode)) return false; + + assignStatement = returnStmtNodeBody(variableNode); + if(size(assignStatement[..]) != 2) return false; + + possiblePhiFunction = assignStatement[1]; + switch(possiblePhiFunction) { + case phiFunction(_, _): return true; + case phiFunction(arrayRef(_, _), _): return false; + default: return false; + } +} + +public bool isVariable(Node graphNode) { + if (size(graphNode[..]) == 0) return false; + + assignStatement = returnStmtNodeBody(graphNode); + variableArg = assignStatement[0]; + typeOfVariableArg = typeOf(variableArg); + + if (size(typeOfVariableArg[..]) == 0) return false; + + return typeOfVariableArg.name == "Variable"; +} + +public bool isSameVariable(Node graphNode, Variable variable) { + if (size(graphNode[..]) == 0) return false; + + assignStatement = returnStmtNodeBody(graphNode); + variableArg = assignStatement[0]; + typeOfVariableArg = typeOf(variableArg); + + if (size(typeOfVariableArg[..]) == 0) return false; + + return variableArg == variable; +} + +public bool isNonAssignmentStatementToRename(Node graphNode) { + return !isReplaced(graphNode) && !isOrdinaryAssignment(graphNode) && !ignoreNode(graphNode) && !isPhiFunctionAssigment(graphNode); +} + +public int returnAssignmentQuantity(Immediate immediate) { + str name = returnVariableImmediateName(immediate); + + if(name in VARIABLE_ASSIGNMENT_COUNT) return VARIABLE_ASSIGNMENT_COUNT[name]; + + VARIABLE_ASSIGNMENT_COUNT[name] = 0; + return VARIABLE_ASSIGNMENT_COUNT[name]; +} + +public int iterateAssignmentQuantity(Immediate immediate) { + str name = returnVariableImmediateName(immediate); + + VARIABLE_ASSIGNMENT_COUNT[name] = VARIABLE_ASSIGNMENT_COUNT[name] + 1; + + return VARIABLE_ASSIGNMENT_COUNT[name]; +} + +public str stackVariableVersion(Immediate immediate, int renameIndex) { + str name = returnVariableImmediateName(immediate); + + VARIABLE_VERSION_STACK[name] = name in VARIABLE_VERSION_STACK ? push(renameIndex, VARIABLE_VERSION_STACK[name]) : push(0, emptyStack()); + + return name; +} + +public int getVariableVersionStacked(Immediate immediate) { + str name = returnVariableImmediateName(immediate); + + if(name in VARIABLE_VERSION_STACK) return peekIntValue(VARIABLE_VERSION_STACK[name]); + + VARIABLE_VERSION_STACK[name] = push(0, emptyStack()); + return 0; +} + +public Stack[int] popOldNode(Node oldNode) { + Variable V = returnStmtVariable(oldNode); + Immediate localVariableImmediate = local(V[0]); + + str name = returnVariableImmediateName(localVariableImmediate); + newStackTuple = pop(VARIABLE_VERSION_STACK[name])[1]; + VARIABLE_VERSION_STACK[name] = newStackTuple; + + return newStackTuple; +} diff --git a/src/main/rascal/lang/jimple/util/Maybe.rsc b/src/main/rascal/lang/jimple/util/Maybe.rsc new file mode 100644 index 00000000..db7b7c5f --- /dev/null +++ b/src/main/rascal/lang/jimple/util/Maybe.rsc @@ -0,0 +1,8 @@ +// https://github.com/UnB-CIC/sle-course/blob/master/sample-code/oberon/src/lang/util/Maybe.rsc +module lang::jimple::util::Maybe + +data Maybe[&T] = nothing() + | just(&T v); + +bool isJust(nothing()) = false; +bool isJust(just(_)) = true; \ No newline at end of file diff --git a/src/main/rascal/lang/jimple/util/Stack.rsc b/src/main/rascal/lang/jimple/util/Stack.rsc new file mode 100644 index 00000000..13dfa3e0 --- /dev/null +++ b/src/main/rascal/lang/jimple/util/Stack.rsc @@ -0,0 +1,28 @@ +module lang::jimple::util::Stack + +import lang::jimple::util::Maybe; + +// Source: https://github.com/UnB-CIC/sle-course/blob/master/sample-code/oberon/src/lang/util/Stack.rsc + +data Stack[&T] = emptyStack() | push(&T v, Stack[&T] s); + +int size(emptyStack()) = 0; +int size(push(v,r)) = 1 + size(r); + +Maybe[&T] peek(emptyStack()) = nothing(); +Maybe[&T] peek(push(v, r)) = just(v); + +tuple[Maybe[&T] v, Stack[&T] stack] pop(Stack[&T] s) { + switch(s) { + case emptyStack() : return ; + case push(v, r) : return ; + } + + return ; +} + +public int peekIntValue(Stack[int] stack) { + switch(peek(stack)[0]) { + case int stackValue: return stackValue; + } +} \ No newline at end of file diff --git a/src/test/java/samples/ssa/math_problems/ApplyPower.java b/src/test/java/samples/ssa/math_problems/ApplyPower.java new file mode 100644 index 00000000..5965bf1b --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/ApplyPower.java @@ -0,0 +1,23 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-16.php +public class ApplyPower { + public static int power(int b, int e) + { + if (e == 0) + return 1; + + int result = b; + int temp = b; + int i, j; + + for (i = 1; i < e; i++) { + for (j = 1; j < b; j++) { + result += temp; + } + temp = result; + } + + return result; + } +} diff --git a/src/test/java/samples/ssa/math_problems/BabylonianSquareRoot.java b/src/test/java/samples/ssa/math_problems/BabylonianSquareRoot.java new file mode 100644 index 00000000..02f55565 --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/BabylonianSquareRoot.java @@ -0,0 +1,15 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-14.php +public class BabylonianSquareRoot { + public static float square_Root(float num) { + float a = num; + float b = 1; + double e = 0.000001; + while (a - b > e) { + a = (a + b) / 2; + b = num / a; + } + return a; + } +} diff --git a/src/test/java/samples/ssa/math_problems/BinomialCoefficient.java b/src/test/java/samples/ssa/math_problems/BinomialCoefficient.java new file mode 100644 index 00000000..8325470f --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/BinomialCoefficient.java @@ -0,0 +1,15 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-24.php +public class BinomialCoefficient { + public static long binomial_Coefficient(int n, int k) + { + if (k>n-k) + k=n-k; + + long result = 1; + for (int i=1, m=n; i<=k; i++, m--) + result = result*m/i; + return result; + } +} diff --git a/src/test/java/samples/ssa/math_problems/Fibonacci.java b/src/test/java/samples/ssa/math_problems/Fibonacci.java new file mode 100644 index 00000000..1648dc38 --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/Fibonacci.java @@ -0,0 +1,14 @@ +package samples.ssa.math_problems; + +public class Fibonacci { + public void run(int maxNumber) { + int previousNumber = 0; + int nextNumber = 1; + + for (int i = 1; i <= maxNumber; ++i) { + int sum = previousNumber + nextNumber; + previousNumber = nextNumber; + nextNumber = sum; + } + } +} diff --git a/src/test/java/samples/ssa/math_problems/FindArrayAverageValue.java b/src/test/java/samples/ssa/math_problems/FindArrayAverageValue.java new file mode 100644 index 00000000..f9a9b422 --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/FindArrayAverageValue.java @@ -0,0 +1,18 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-17.php +public class FindArrayAverageValue { + // Prints average of a stream of numbers + static float streamAvg() + { + float arr[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; + int n = arr.length; + float avg = 0; + for (int i = 0; i < n; i++) + { + //avg = getAvg(avg, arr[i], i); + avg = (avg * i + arr[i]) / (i + 1); + } + return avg; + } +} diff --git a/src/test/java/samples/ssa/math_problems/GeneratePrimes.java b/src/test/java/samples/ssa/math_problems/GeneratePrimes.java new file mode 100644 index 00000000..6811e8bd --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/GeneratePrimes.java @@ -0,0 +1,26 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-20.php +// This code trigges a decompiler bug, issue https://github.com/PAMunb/JimpleFramework/issues/37 +public class GeneratePrimes { + public static int [] generatePrimes(int num) { + boolean[] temp = new boolean[num + 1]; + for (int i = 2; i * i <= num; i++) { + if (!temp [i]) { + for (int j = i; i * j <= num; j++) { + temp [i*j] = true; + } + } + } + int prime_nums = 0; + for (int i = 2; i <= num; i++) { + if (!temp [i]) prime_nums++; + } + int [] all_primes = new int [prime_nums]; + int index = 0; + for (int i = 2; i <= num; i++) { + if (!temp [i]) all_primes [index++] = i; + } + return all_primes; + } +} diff --git a/src/test/java/samples/ssa/math_problems/GetFractionalPart.java b/src/test/java/samples/ssa/math_problems/GetFractionalPart.java new file mode 100644 index 00000000..1cc1142d --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/GetFractionalPart.java @@ -0,0 +1,10 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-2.php +public class GetFractionalPart { + public static void main(String[] args) { + double value = 12.56; + double fractional_part = value % 1; + double integral_part = value - fractional_part; + } +} diff --git a/src/test/java/samples/ssa/math_problems/ReverseInteger.java b/src/test/java/samples/ssa/math_problems/ReverseInteger.java new file mode 100644 index 00000000..2f9707c8 --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/ReverseInteger.java @@ -0,0 +1,27 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-6.php +public class ReverseInteger { + public static void main(String[] args) { + int num =1287; + int is_positive = 1; + if (num < 0) { + is_positive = -1; + num = -1 * num; + } + + int sum = 0; + + while (num > 0) { + int r = num % 10; + + int maxDiff = Integer.MAX_VALUE - sum * 10; + if (sum > Integer.MAX_VALUE / 10 || r > maxDiff); + + sum = sum * 10 + r; + num /= 10; + } + + int result = is_positive * sum; + } +} diff --git a/src/test/java/samples/ssa/math_problems/RoundUpDivision.java b/src/test/java/samples/ssa/math_problems/RoundUpDivision.java new file mode 100644 index 00000000..2e176ab0 --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/RoundUpDivision.java @@ -0,0 +1,9 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-4.php +public class RoundUpDivision { + public static void main(String[] args) { + int tot_theory_marks = 350, tot_practical_marks = 90, tot_marks = 500; + int percentage_of_marks = ((tot_theory_marks + tot_practical_marks) * 100) / tot_marks; + } +} diff --git a/src/test/java/samples/ssa/math_problems/TaylorExponential.java b/src/test/java/samples/ssa/math_problems/TaylorExponential.java new file mode 100644 index 00000000..855aa8cc --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/TaylorExponential.java @@ -0,0 +1,14 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-25.php +public class TaylorExponential { + static float taylorExponential(int n, float x) + { + float exp_sum = 1; + + for (int i = n - 1; i > 0; --i ) + exp_sum = 1 + x * exp_sum / i; + + return exp_sum; + } +} diff --git a/src/test/java/samples/ssa/math_problems/ValidateDoubleIsInteger.java b/src/test/java/samples/ssa/math_problems/ValidateDoubleIsInteger.java new file mode 100644 index 00000000..8edaf88b --- /dev/null +++ b/src/test/java/samples/ssa/math_problems/ValidateDoubleIsInteger.java @@ -0,0 +1,15 @@ +package samples.ssa.math_problems; + +// Source: https://www.w3resource.com/java-exercises/math/java-math-exercise-3.php +public class ValidateDoubleIsInteger { + public static void main(String[] args) { + double d_num = 5.44444; + Boolean result; + + if ((d_num % 1) == 0) { + result = false; + } else { + result = true; + } + } +} diff --git a/src/test/java/samples/ssa/other/SimpleException.java b/src/test/java/samples/ssa/other/SimpleException.java new file mode 100644 index 00000000..f9c10106 --- /dev/null +++ b/src/test/java/samples/ssa/other/SimpleException.java @@ -0,0 +1,18 @@ +package samples.ssa.other; + +public class SimpleException { + public SimpleException() { + } + + public static void run() { + try { + raiseException(); + } catch(Exception e) { + System.out.println(e); + } + } + + public static void raiseException() throws Exception { + throw new Exception("An exception"); + } +} diff --git a/src/test/java/samples/ssa/other/SootExampleCode.java b/src/test/java/samples/ssa/other/SootExampleCode.java new file mode 100644 index 00000000..9abf36b9 --- /dev/null +++ b/src/test/java/samples/ssa/other/SootExampleCode.java @@ -0,0 +1,18 @@ +package samples.ssa.other; + +public class SootExampleCode { + + // https://github.com/soot-oss/soot/tree/master/src/main/java/soot/shimple + public int test() { + int x = 100; + + while(x == 100){ + if(x < 200) + x = 100; + else + x = 200; + } + + return x; + } +} diff --git a/src/test/java/samples/ssa/sort/BubbleSort.java b/src/test/java/samples/ssa/sort/BubbleSort.java new file mode 100644 index 00000000..2e3b2480 --- /dev/null +++ b/src/test/java/samples/ssa/sort/BubbleSort.java @@ -0,0 +1,21 @@ +package samples.ssa.sort; + +// Source: https://www.javatpoint.com/bubble-sort-in-java +public class BubbleSort { + static void bubbleSort(int[] arr) { + int n = arr.length; + int temp = 0; + for(int i=0; i < n; i++){ + for(int j=1; j < (n-i); j++){ + if(arr[j-1] > arr[j]){ + //swap elements + temp = arr[j-1]; + arr[j-1] = arr[j]; + arr[j] = temp; + } + + } + } + + } +} diff --git a/src/test/java/samples/ssa/sort/QuickSort.java b/src/test/java/samples/ssa/sort/QuickSort.java new file mode 100644 index 00000000..1fd22669 --- /dev/null +++ b/src/test/java/samples/ssa/sort/QuickSort.java @@ -0,0 +1,82 @@ +package samples.ssa.sort; + +// Source: https://www.geeksforgeeks.org/java-program-for-quicksort/ + +// Java program for implementation of QuickSort +class QuickSort { + /* This function takes last element as pivot, + places the pivot element at its correct + position in sorted array, and places all + smaller (smaller than pivot) to left of + pivot and all greater elements to right + of pivot */ + int partition(int arr[], int low, int high) + { + int pivot = arr[high]; + int i = (low-1); // index of smaller element + for (int j=low; j Array to be sorted, + low --> Starting index, + high --> Ending index */ + void sort(int arr[], int low, int high) + { + if (low < high) + { + /* pi is partitioning index, arr[pi] is + now at right place */ + int pi = partition(arr, low, high); + + // Recursively sort elements before + // partition and after partition + sort(arr, low, pi-1); + sort(arr, pi+1, high); + } + } + + /* A utility function to print array of size n */ + static void printArray(int arr[]) + { + int n = arr.length; + for (int i=0; i, + + , + + , + + , + + , + + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} \ No newline at end of file diff --git a/src/test/rascal/ssa/SSAIntegrationTests/sort/TestBubbleSort.rsc b/src/test/rascal/ssa/SSAIntegrationTests/sort/TestBubbleSort.rsc new file mode 100644 index 00000000..376b8cff --- /dev/null +++ b/src/test/rascal/ssa/SSAIntegrationTests/sort/TestBubbleSort.rsc @@ -0,0 +1,14 @@ +module ssa::SSAIntegrationTests::sort::TestBubbleSort + +import Set; +import lang::jimple::toolkit::FlowGraph; +import ssa::SSAIntegrationTests::util::CreateClassfileSSAFlowGraph; + +test bool testBubbleSortMethod() { + loc classFilePath = |project://JimpleFramework/target/test-classes/samples/ssa/sort/BubbleSort.class|; + int methodToTest = 1; + FlowGraph result = createClassFileSSAFlowGraph(classFilePath, methodToTest); + + // Flow Graph creation error + return !isEmpty(result); +} \ No newline at end of file diff --git a/src/test/rascal/ssa/SSAIntegrationTests/sort/TestQuickSort.rsc b/src/test/rascal/ssa/SSAIntegrationTests/sort/TestQuickSort.rsc new file mode 100644 index 00000000..2871e56a --- /dev/null +++ b/src/test/rascal/ssa/SSAIntegrationTests/sort/TestQuickSort.rsc @@ -0,0 +1,41 @@ +module ssa::SSAIntegrationTests::sort::TestQuickSort + +import Set; +import lang::jimple::toolkit::FlowGraph; +import ssa::SSAIntegrationTests::util::CreateClassfileSSAFlowGraph; + +test bool testQuickSortPartitionMethod() { + loc classFilePath = |project://JimpleFramework/target/test-classes/samples/ssa/sort/QuickSort.class|; + int methodToTest = 1; + FlowGraph result = createClassFileSSAFlowGraph(classFilePath, methodToTest); + + // Flow Graph creation error + return !isEmpty(result); +} + +test bool testQuickSortSortMethod() { + loc classFilePath = |project://JimpleFramework/target/test-classes/samples/ssa/sort/QuickSort.class|; + int methodToTest = 2; + FlowGraph result = createClassFileSSAFlowGraph(classFilePath, methodToTest); + + // Doest support invokeStmt operators + return result == {}; +} + +test bool testQuickSortPrintMethod() { + loc classFilePath = |project://JimpleFramework/target/test-classes/samples/ssa/sort/QuickSort.class|; + int methodToTest = 3; + FlowGraph result = createClassFileSSAFlowGraph(classFilePath, methodToTest); + + // Doest support invokeStmt operators + return result == {}; +} + +test bool testQuickSortMainMethod() { + loc classFilePath = |project://JimpleFramework/target/test-classes/samples/ssa/sort/QuickSort.class|; + int methodToTest = 4; + FlowGraph result = createClassFileSSAFlowGraph(classFilePath, methodToTest); + + // Doest support invokeStmt operators + return result == {}; +} \ No newline at end of file diff --git a/src/test/rascal/ssa/SSAIntegrationTests/util/CreateClassfileSSAFlowGraph.rsc b/src/test/rascal/ssa/SSAIntegrationTests/util/CreateClassfileSSAFlowGraph.rsc new file mode 100644 index 00000000..aab088a8 --- /dev/null +++ b/src/test/rascal/ssa/SSAIntegrationTests/util/CreateClassfileSSAFlowGraph.rsc @@ -0,0 +1,37 @@ +module ssa::SSAIntegrationTests::util::CreateClassfileSSAFlowGraph + +import List; +import Type; +import Set; + +import analysis::graphs::Graph; + +import lang::jimple::decompiler::Decompiler; +import lang::jimple::decompiler::jimplify::ProcessLabels; +import lang::jimple::core::Context; +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; + +import lang::jimple::toolkit::ssa::Generator; + +public FlowGraph createClassFileSSAFlowGraph(loc classFilePath, int methodToReturn) { + loc classFilePath = classFilePath; + ClassOrInterfaceDeclaration classFileDeclaration = decompile(classFilePath); + classFileDeclaration = processJimpleLabels(classFileDeclaration); + list[Method] methodList = castMethodList(classFileDeclaration); + MethodBody methodBody = castMethodBody(methodList[methodToReturn]); + + return applySSATransformation(methodBody); +} + +private list[Method] castMethodList(ClassOrInterfaceDeclaration declaration) { + switch(declaration[5]) { + case list[Method] methodList : return methodList; + } +} + +private MethodBody castMethodBody(Method method) { + switch(method[5]) { + case MethodBody methodBody : return methodBody; + } +} \ No newline at end of file diff --git a/src/test/rascal/ssa/TestDominanceFrontier.rsc b/src/test/rascal/ssa/TestDominanceFrontier.rsc new file mode 100644 index 00000000..59fdc3a3 --- /dev/null +++ b/src/test/rascal/ssa/TestDominanceFrontier.rsc @@ -0,0 +1,38 @@ +module ssa::TestDominanceFrontier + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::toolkit::ssa::DominanceFrontier; +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::core::Syntax; + +test bool testDominanceFrontier() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v1"), immediate(iValue(intValue(2)))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + dominanceTree = createDominanceTree(flowGraph); + + result = createDominanceFrontier(flowGraph, dominanceTree); + + return result == ( + stmtNode(assign(localVariable("v1"),immediate(iValue(intValue(2))))):{ + stmtNode(gotoStmt("print")) + }, + stmtNode(assign(localVariable("v1"),immediate(iValue(intValue(1))))):{ + stmtNode(gotoStmt("print")) + } + ); +} diff --git a/src/test/rascal/ssa/TestDominanceTree.rsc b/src/test/rascal/ssa/TestDominanceTree.rsc new file mode 100644 index 00000000..da5155fd --- /dev/null +++ b/src/test/rascal/ssa/TestDominanceTree.rsc @@ -0,0 +1,118 @@ +module ssa::TestDominanceTree + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::core::Syntax; + +test bool testDominanceTree() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("label2:"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v2"), immediate(iValue(intValue(2)))); + + Statement s7 = label("label2:"); + Statement s8 = label("print"); + + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + + map[&T, set[&T]] expectation = ( + stmtNode(s6): { stmtNode(s9), exitNode() }, + entryNode(): { + stmtNode(s6), + stmtNode(s2), + stmtNode(s9), + stmtNode(s4), + stmtNode(s1), + stmtNode(s3), + exitNode(), + stmtNode(s8) + }, + stmtNode(s2): { + stmtNode(s6), + stmtNode(s9), + stmtNode(s4), + stmtNode(s3), + exitNode(), + stmtNode(s8) + }, + stmtNode(s9): {exitNode()}, + stmtNode(s4): {stmtNode(s8)}, + stmtNode(s1): { + stmtNode(s6), + stmtNode(s2), + stmtNode(s9), + stmtNode(s4), + stmtNode(s3), + exitNode(), + stmtNode(s8) + }, + stmtNode(s3): { + stmtNode(s4), + stmtNode(s8) + }, + exitNode(): {}, + stmtNode(s8): {} + ); + + return createDominanceTree(flowGraph) == expectation; +} + + +test bool testFindIdomForInitialNode() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("label2:"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v2"), immediate(iValue(intValue(2)))); + + Statement s7 = label("label2:"); + Statement s8 = label("print"); + + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + + return findIdom(dominanceTree, stmtNode(s1)) == entryNode(); +} + +test bool testFindIdomForAMiddleNode() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("label2:"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v2"), immediate(iValue(intValue(2)))); + + Statement s7 = label("label2:"); + Statement s8 = label("print"); + + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + + return findIdom(dominanceTree, stmtNode(s9)) == stmtNode(s6); +} diff --git a/src/test/rascal/ssa/TestPhiFunctionInsertion.rsc b/src/test/rascal/ssa/TestPhiFunctionInsertion.rsc new file mode 100644 index 00000000..e61fcb22 --- /dev/null +++ b/src/test/rascal/ssa/TestPhiFunctionInsertion.rsc @@ -0,0 +1,131 @@ +module ssa::TestPhiFunctionInsertion + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::toolkit::ssa::DominanceFrontier; +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::toolkit::ssa::PhiFunctionInsertion; +import lang::jimple::core::Syntax; + +test bool testDoesNotInserntInSimpleAssigment() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + Statement s2 = assign(localVariable("v1"), immediate(local("v0"))); + + list[Statement] stmts = [s1, s2]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + return phiFunctionFlowGraph == flowGraph; +} + +test bool testPhiFunctionInsertion() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v1"), immediate(iValue(intValue(2)))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = returnStmt(local("v1")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + dominanceTree = createDominanceTree(flowGraph); + + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + + result = insertPhiFunctions(flowGraph, dominanceFrontier); + + return result == { + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} + +test bool testPhiFunctionInsertionForArray() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(intValue(1)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(arrayRef("v1", local("v0")), immediate(local("r0"))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(arrayRef("v1", local("v0")), immediate(local("r1"))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + + result = insertPhiFunctions(flowGraph, dominanceFrontier); + + return result == { + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} diff --git a/src/test/rascal/ssa/TestVariableRenaming.rsc b/src/test/rascal/ssa/TestVariableRenaming.rsc new file mode 100644 index 00000000..3af07505 --- /dev/null +++ b/src/test/rascal/ssa/TestVariableRenaming.rsc @@ -0,0 +1,269 @@ +module ssa::TestVariableRenaming + +import lang::jimple::toolkit::FlowGraph; +import lang::jimple::core::Syntax; +import lang::jimple::toolkit::ssa::DominanceTree; +import lang::jimple::toolkit::ssa::PhiFunctionInsertion; +import lang::jimple::toolkit::ssa::DominanceFrontier; +import lang::jimple::toolkit::ssa::VariableRenaming; + +test bool testLeftHandSideImmediatesRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + Statement s2 = assign(localVariable("v1"), immediate(iValue(booleanValue(true)))); + + list[Statement] stmts = [s1, s2]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + , + + }; +} + +test bool testRightHandSideImmediatesRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + Statement s2 = assign(localVariable("v1"), immediate(local("v0"))); + + list[Statement] stmts = [s1, s2]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + , + + }; +} + +test bool testRightHandMultipleImmediatesRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + Statement s2 = assign(localVariable("v0"), immediate(local("v0"))); + Statement s3 = assign(localVariable("v0"), immediate(local("v0"))); + + list[Statement] stmts = [s1, s2]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + , + + }; +} + +test bool testLeftHandMultipleImmediatesRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + Statement s2 = assign(localVariable("v0"), immediate(iValue(booleanValue(true)))); + + list[Statement] stmts = [s1, s2]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + , + + }; +} + +test bool testPhiFunctionArgumentsRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(booleanValue(false)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(localVariable("v1"), immediate(iValue(intValue(1)))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(localVariable("v1"), immediate(iValue(intValue(2)))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = returnStmt(local("v1")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} + +test bool testPhiFunctionArrayArgumentsRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(intValue(1)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(arrayRef("v1", local("v0")), immediate(local("r0"))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(arrayRef("v1", local("v0")), immediate(local("r1"))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = returnStmt(local("v2")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} + + +test bool testPhiFunctionArraySubsArgumentsRename() { + Statement s1 = assign(localVariable("v0"), immediate(iValue(intValue(1)))); + + Statement s2 = ifStmt(cmp(local("v0"), iValue(booleanValue(false))), "label1:"); + Statement s3 = assign(arrayRef("v1", local("v0")), immediate(local("r0"))); + Statement s4 = gotoStmt("print"); + + Statement s5 = label("label1:"); + Statement s6 = assign(arrayRef("v1", local("v0")), immediate(local("r1"))); + Statement s7 = gotoStmt("print"); + + Statement s8 = label("print"); + Statement s9 = assign(localVariable("v3"), arraySubscript("v1", local("v0"))); + Statement s10 = returnStmt(local("v3")); + + list[Statement] stmts = [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10]; + + methodStatments = methodBody([], stmts, []); + flowGraph = forwardFlowGraph(methodStatments); + map[&T, set[&T]] dominanceTree = createDominanceTree(flowGraph); + map[&T, set[&T]] dominanceFrontier = createDominanceFrontier(flowGraph, dominanceTree); + FlowGraph phiFunctionFlowGraph = insertPhiFunctions(flowGraph, dominanceFrontier); + + result = applyVariableRenaming(phiFunctionFlowGraph); + + return result == { + , + + , + + , + + , + + , + + , + + , + + , + + , + + + }; +} \ No newline at end of file diff --git a/src/test/rascal/util/TestStack.rsc b/src/test/rascal/util/TestStack.rsc new file mode 100644 index 00000000..afc21fa3 --- /dev/null +++ b/src/test/rascal/util/TestStack.rsc @@ -0,0 +1,15 @@ +module util::TestStack + +import lang::jimple::util::Maybe; +import lang::jimple::util::Stack; + +Stack[int] stack = push(1, push(2, emptyStack())); + +test bool emptySizeTest() = 0 == size(emptyStack()); +test bool sizeTest() = 2 == size(stack); + +test bool emptyPopTest() = == pop(emptyStack()); +test bool popTest() = == pop(stack); + +test bool emptyPeekTest() = nothing() == peek(emptyStack()); +test bool peekTest() = just(1) == peek(stack); \ No newline at end of file