private bool TryOptimizeCompareWithZero(ILCompilationUnit unit, ILFlagsVariable fl, ILVariableExpression flUsage) { if (fl.ImplicitAssignments.Count != 1) { return(false); } var flAssignment = fl.ImplicitAssignments.First(); MatchResult match; if ((match = ComparePattern.Match(flAssignment)).Success) // (cmp(a, b); equals(FL, 0)) <=> (a == b) { return(TryOptimizeToEquals(unit, fl, flUsage, flAssignment, match)); } if ((match = BigRelationalPattern.Match(flAssignment)).Success) { return(TryOptimizeToLessThan(unit, fl, flUsage, flAssignment, match)); } if ((match = AndRegistersPattern.Match(flAssignment)).Success) { return(TryOptimizeToGreaterThan(unit, fl, flUsage, flAssignment, match)); } return(false); }
private bool TryOptimizeToEquals( ILCompilationUnit unit, ILFlagsVariable fl, ILVariableExpression flUsage, ILExpression flAssignment, MatchResult match) { // Replace FL operation with the new comparison operation. ILOpCode opcode; switch (((ILInstructionExpression)flAssignment).OpCode.Code) { case ILCode.CMP: opcode = ILOpCodes.__EQUALS_OBJECT; break; case ILCode.CMP_R32: opcode = ILOpCodes.__EQUALS_R32; break; case ILCode.CMP_R64: opcode = ILOpCodes.__EQUALS_R64; break; case ILCode.CMP_DWORD: opcode = ILOpCodes.__EQUALS_DWORD; break; case ILCode.CMP_QWORD: opcode = ILOpCodes.__EQUALS_QWORD; break; default: return(false); } // Introduce new variable for storing the result of the comparison. var resultVar = unit.GetOrCreateVariable($"simplified_{fl.Name}"); resultVar.VariableType = VMType.Dword; var assignment = new ILAssignmentStatement(resultVar, new ILInstructionExpression(-1, opcode, null, VMType.Dword) { Arguments = { (ILExpression)match.Captures["left"][0].Remove(), (ILExpression)match.Captures["right"][0].Remove() } }); fl.ImplicitAssignments.Remove(flAssignment); flAssignment.Parent.ReplaceWith(assignment); // Replace FL reference with new variable. flUsage.Variable = null; flUsage.Parent.Parent.ReplaceWith(new ILVariableExpression(resultVar)); return(true); }
private static void ReplaceRawLocalReferences(ILCompilationUnit unit, ILogger logger) { // Find in each block the patterns for loading and/or storing local variable values, // and replace them with a normal variable expression or assignment statement. foreach (var node in unit.ControlFlowGraph.Nodes) { var block = (ILAstBlock)node.UserData[ILAstBlock.AstBlockProperty]; for (int i = 0; i < block.Statements.Count; i++) { // TODO: Might need some extra checks after the match of a pattern. // Variables referenced in the expressions are not checked for // equality. MatchResult match; if ((match = StoreToLocalPattern.Match(block.Statements, i)).Success) { ReplaceStoreToLocal(unit, match, logger); } else if ((match = LoadLocalPattern.Match(block.Statements, i)).Success) { ReplaceLoadToLocal(unit, match, logger); } else if ((match = LoadLocalRefPattern.Match(block.Statements, i)).Success) { ReplaceLoadLocalRef(unit, match, logger); } } } }
public bool ApplyTransformation(ILCompilationUnit unit, ILogger logger) { int iteration = 0; bool changed = true; while (changed && iteration < MaxIterations) { iteration++; logger.Debug2(Name, $"Started iteration {iteration.ToString()}..."); OnIterationStart(); changed = PerformSingleIteration(unit, logger, iteration); logger.Debug2(Name, $"Finished iteration {iteration.ToString()} (AST has changed: {changed.ToString()})."); OnIterationEnd(); } if (iteration == MaxIterations && changed) { logger.Warning(Name, $"Reached maximum amount of iterations of {MaxIterations.ToString()} and AST is " + "still changing. This might be a bug in the transformer pipeline where transforms keep " + "cancelling each other out, or the method to devirtualise is too complex for the provided " + "upper bound of iterations."); } return(iteration > 1); }
private static ILVariable ResolveVariable(ILCompilationUnit unit, int offset, ILogger logger) { var field = unit.FrameLayout.Resolve(offset); if (!field.IsValid) { switch (field.FieldKind) { case FrameFieldKind.Parameter: logger.Warning(Tag, $"Reference to non-existing parameter {field.Index} detected."); break; case FrameFieldKind.ReturnAddress: logger.Warning(Tag, $"Reference to return address detected."); break; case FrameFieldKind.CallersBasePointer: logger.Warning(Tag, $"Reference to callers base pointer detected."); break; case FrameFieldKind.LocalVariable: logger.Warning(Tag, $"Reference to non-existing local variable {field.Index} detected."); break; default: throw new ArgumentOutOfRangeException(); } } return(unit.GetOrCreateVariable(field)); }
private static void DetermineAndDeclareLocals(ILCompilationUnit unit) { int localsCount = DetermineLocalCountFromPrologue(unit); for (int i = 0; i < localsCount; i++) { unit.GetOrCreateVariable("local_" + i); } }
public void ApplyTransformation(ILCompilationUnit unit, ILogger logger) { var classes = ObtainPhiCongruenceClasses(unit); foreach (var @class in classes) { @class.ReplaceVarsWithRepresentative(); @class.RemovePhiNodes(); } }
public virtual bool VisitCompilationUnit(ILCompilationUnit unit) { bool changed = false; foreach (var node in unit.ControlFlowGraph.Nodes) { var block = (ILAstBlock)node.UserData[ILAstBlock.AstBlockProperty]; changed |= block.AcceptVisitor(this); } return(changed); }
public virtual bool ApplyTransformation(ILCompilationUnit unit, ILogger logger) { bool changed = false; while (unit.AcceptVisitor(this)) { changed = true; // Repeat until no more changes. } return(changed); }
private static int DetermineLocalCountFromPrologue(ILCompilationUnit unit) { var entryBlock = (ILAstBlock)unit.ControlFlowGraph.Entrypoint.UserData[ILAstBlock.AstBlockProperty]; var match = AllocateLocalsPattern.FindMatch(entryBlock.Statements); if (match.Success) { var pushLocalCount = (ILInstructionExpression)match.Captures["push_local_count"][0]; return(Convert.ToInt32(pushLocalCount.Operand)); } return(0); }
private bool TryOptimizeFlagComparison(ILCompilationUnit unit, ILVariableExpression flagUsage, ILFlagsVariable fl, VMFlags flags) { // TODO: support other flag combinations. bool changed = false; switch (flags) { case VMFlags.ZERO: changed |= TryOptimizeCompareWithZero(unit, fl, flagUsage); break; } return(changed); }
private bool PerformSingleIteration(ILCompilationUnit unit, ILogger logger, int iterationNumber) { bool changed = false; foreach (var transform in Transforms) { logger.Debug2(Name, "Applying " + transform.Name + "..."); OnTransformStart(new ILTransformEventArgs(unit, transform, iterationNumber)); changed |= transform.ApplyTransformation(unit, logger); OnTransformEnd(new ILTransformEventArgs(unit, transform, iterationNumber)); } return(changed); }
private static void RemoveSPAssignments(ILCompilationUnit unit) { // We assume all stack related operations are already handled. // We can therefore safely remove any reference to SP. var sp = unit.GetOrCreateVariable("SP"); foreach (var assign in sp.AssignedBy.ToArray()) { assign.Variable = null; foreach (var use in assign.Value.AcceptVisitor(VariableUsageCollector.Instance)) { use.Variable = null; } assign.Remove(); } }
public void VisitCompilationUnit(ILCompilationUnit unit) { _offsets.Clear(); foreach (var variable in unit.Variables.OfType <ILFlagsVariable>()) { foreach (int dataSource in variable.DataSources) { _offsets.Add(dataSource, variable); } } foreach (var node in unit.ControlFlowGraph.Nodes) { var block = (ILAstBlock)node.UserData[ILAstBlock.AstBlockProperty]; block.AcceptVisitor(this); } }
private static void ReplaceLoadToLocal(ILCompilationUnit unit, MatchResult match, ILogger logger) { // Obtain variable that is referenced. var pushOffset = (ILInstructionExpression)match.Captures["push_offset"][0]; int offset = unchecked ((int)Convert.ToUInt32(pushOffset.Operand)); var variable = ResolveVariable(unit, offset, logger); // Remove the original expression containing the address and unregister the // associated variable. var lindExpr = (ILInstructionExpression)match.Captures["load"][0]; var address = (ILVariableExpression)lindExpr.Arguments[0].Remove(); address.Variable = null; // Replace with normal variable expression. lindExpr.ReplaceWith(new ILVariableExpression(variable)); }
private static void ReplaceLoadLocalRef(ILCompilationUnit unit, MatchResult match, ILogger logger) { // Obtain variable that is referenced. var pushOffset = (ILInstructionExpression)match.Captures["push_offset"][0]; int offset = unchecked ((int)Convert.ToUInt32(pushOffset.Operand)); var variable = ResolveVariable(unit, offset, logger); // Obtain reference to final loaded value of the variable. var finalValue = (ILVariableExpression)match.Captures["final_value"][0]; finalValue.Variable = null; // Replace with normal variable expression. finalValue.ReplaceWith(new ILVariableExpression(variable) { IsReference = true, ExpressionType = VMType.Object }); }
private static void ReplaceStoreToLocal(ILCompilationUnit unit, MatchResult match, ILogger logger) { // Obtain variable that is referenced. var pushOffset = (ILInstructionExpression)match.Captures["push_offset"][0]; int offset = unchecked ((int)Convert.ToUInt32(pushOffset.Operand)); var variable = ResolveVariable(unit, offset, logger); // Obtain SIND_xxxx expression. var statement = (ILExpressionStatement)match.Captures["store"][0]; var sindExpr = (ILInstructionExpression)statement.Expression; // Remove value. var value = (ILExpression)sindExpr.Arguments[0].Remove(); // Remove the original expression containing the address and unregister the // associated variable. var address = (ILVariableExpression)sindExpr.Arguments[0].Remove(); address.Variable = null; // Replace with normal assignment. statement.ReplaceWith(new ILAssignmentStatement(variable, value)); }
public override bool VisitCompilationUnit(ILCompilationUnit unit) { bool changed = false; bool continueLoop = true; while (continueLoop) { continueLoop = false; foreach (var variable in unit.Variables.OfType <ILFlagsVariable>()) { if (variable.UsedBy.Count == 1) { var use = variable.UsedBy[0]; var andExpression = use.Parent.Parent; var andMatch = FLAndConstantPattern.Match(andExpression); if (andMatch.Success) { var fl = (ILFlagsVariable)((ILVariableExpression)andMatch.Captures["fl"][0]).Variable; var constant = (ILInstructionExpression)andMatch.Captures["constant"][0]; var flags = _constants.ToFlags((byte)(uint)constant.Operand); continueLoop = TryOptimizeFlagComparison(unit, use, fl, flags); if (continueLoop) { changed = true; break; } } } } } return(changed); }
void IILAstTransform.ApplyTransformation(ILCompilationUnit unit, ILogger logger) { ApplyTransformation(unit, logger); }
private static IEnumerable <PhiCongruenceClass> ObtainPhiCongruenceClasses(ILCompilationUnit unit) { var classes = new HashSet <PhiCongruenceClass>(); var variableToClass = new Dictionary <ILVariable, PhiCongruenceClass>(); foreach (var variable in unit.Variables.ToArray()) { // Phi nodes are always present in the form: // v = phi(v1, v2, ..., vn) // A situation like the following might happen: // // a3 = phi(a1, a2) // ... // a5 = phi(a3, a5) // // Here, a3 and a5 are connected and form an equivalence class. // if (variable.AssignedBy.Count == 1 && variable.AssignedBy[0].Value is ILPhiExpression phi) { // Check if variable does not belong to any equivalence class already. var connectedVariables = new HashSet <ILVariable>(phi.Variables.Select(x => x.Variable)) { variable }; var congruenceClasses = new List <PhiCongruenceClass>(); foreach (var connectedVar in connectedVariables.ToArray()) { if (variableToClass.TryGetValue(connectedVar, out var congruenceClass)) { // The referenced variable is already part of another class, we need to combine the // two classes together. congruenceClasses.Add(congruenceClass); classes.Remove(congruenceClass); connectedVariables.UnionWith(congruenceClass.Variables); } } PhiCongruenceClass finalClass; if (congruenceClasses.Count == 0) { // No variable was part of a class yet => We need a new one. var representative = unit.GetOrCreateVariable("phi_" + variableToClass.Count); representative.IsVirtual = connectedVariables.First().IsVirtual; representative.VariableType = variable.VariableType; finalClass = new PhiCongruenceClass(representative); } else { // At least one of the variables was part of a class already. // Pick one class that we are going to expand. finalClass = congruenceClasses[0]; } // Add all connected variables to the new class. finalClass.Variables.UnionWith(connectedVariables); foreach (var connectedVar in connectedVariables) { variableToClass[connectedVar] = finalClass; } classes.Add(finalClass); } } return(classes); }
public override bool ApplyTransformation(ILCompilationUnit unit, ILogger logger) { return(base.ApplyTransformation(unit, logger) && unit.RemoveNonUsedVariables()); }
private static Dictionary <Node, ICollection <ILAssignmentStatement> > InsertPhiNodes(ILCompilationUnit unit) { var result = unit.ControlFlowGraph.Nodes.ToDictionary( x => x, x => (ICollection <ILAssignmentStatement>) new List <ILAssignmentStatement>()); // We try to find all variables that have more than one assignment, and therefore have multiple // versions of it during execution of the program. This is only a problem when they have different // values at join nodes, as depicted below. We therefore need to get to the dominance frontier of // those nodes and insert phi nodes there. // // [ x1 <- value1 ] [ x2 <- value2 ] // | | // '------------+-----------' // | // [ x3 <- phi(x1, x2) ] // // Collect all nodes that contain a variable assignment (i.e. a new definition). var variableBlocks = unit.Variables.ToDictionary( x => x, x => new HashSet <Node>(x.AssignedBy.Select(a => a.GetParentNode()))); foreach (var variable in unit.Variables.Where(x => x.AssignedBy.Count > 1 || // If the variable has more than one definition GetNodesReferencingVariable(x).Count() > 1) // Or is used in multiple nodes. ) { var agenda = new Queue <Node>(variableBlocks[variable]); while (agenda.Count > 0) { var current = agenda.Dequeue(); foreach (var frontierNode in unit.DominatorInfo.GetDominanceFrontier(current)) { // If the frontier node does not define a phi node already for this variable, we need to add it. if (result[frontierNode].All(x => x.Variable != variable)) { // Check if the variable is defined in the frontier node. bool defined = variableBlocks[variable].Contains(frontierNode); // Build phi node. // The number of different versions of the variable is equal to the amount of predecessors. var phiExpression = new ILPhiExpression(Enumerable .Repeat(variable, frontierNode.InDegree) .Select(v => new ILVariableExpression(v))); var phiNode = new ILAssignmentStatement(variable, phiExpression); // Insert at top of the current block. var block = (ILAstBlock)frontierNode.UserData[ILAstBlock.AstBlockProperty]; block.Statements.Insert(0, phiNode); // Register phi node. result[frontierNode].Add(phiNode); // We might have to check this node again if we introduce a new version of this variable // at this node. if (!defined) { agenda.Enqueue(frontierNode); } } } } } return(result); }
public void ApplyTransformation(ILCompilationUnit unit, ILogger logger) { var phiNodes = InsertPhiNodes(unit); RenameVariables(unit, phiNodes); }
private static void RenameVariables(ILCompilationUnit unit, IDictionary <Node, ICollection <ILAssignmentStatement> > phiNodes) { // We keep track of two variables for each variable. // - A counter to introduce new variables with new names that are unique throughout the entire function. // - A stack containing the current versions of the variable. var counter = new Dictionary <ILVariable, int>(); var stack = new Dictionary <ILVariable, Stack <ILVariable> >(); foreach (var variable in unit.Variables.Union(unit.Parameters)) { counter[variable] = 0; stack[variable] = new Stack <ILVariable>(); // Note: This is a slight deviation of the original algorithm. // Some variables (such as registers) do not have an initial value specified in the method. // To avoid problems, we add the "global" definition to the stack. stack[variable].Push(variable); } // Start at the entry point of the graph. Rename(unit.ControlFlowGraph.Entrypoint); void Rename(Node n) { var block = (ILAstBlock)n.UserData[ILAstBlock.AstBlockProperty]; var originalVars = new Dictionary <ILAssignmentStatement, ILVariable>(); foreach (var statement in block.Statements) { bool updateVariables = true; if (statement is ILAssignmentStatement assignment) { var variable = assignment.Variable; originalVars.Add(assignment, variable); // We have a new version of a variable. Let's introduce a new version. counter[variable]++; var newVariable = unit.GetOrCreateVariable($"{variable.Name}_v{counter[variable].ToString()}"); newVariable.IsVirtual = variable.IsVirtual; newVariable.VariableType = variable.VariableType; stack[variable].Push(newVariable); // Update the variable in the assignment. assignment.Variable = newVariable; // Don't update arguments of phi nodes. They are updated somewhere else. if (assignment.Value is ILPhiExpression) { updateVariables = false; } } if (updateVariables) { // Update variables inside the statement with the new versions. foreach (var use in statement.AcceptVisitor(VariableUsageCollector.Instance)) { use.Variable = stack[use.Variable].Peek(); } } } // Update phi statements in successor nodes. foreach (var successor in n.GetSuccessors()) { // Determine the index of the phi expression argument. // TODO: Might be inefficient to do an OrderBy every time. // Maybe optimise by ordering (e.g. numbering) the edges beforehand? int argumentIndex = successor.GetPredecessors() .OrderBy(x => x.Name) .ToList() .IndexOf(n); // Update all variables in the phi nodes to the new versions. foreach (var phiNode in phiNodes[successor]) { var phiExpression = (ILPhiExpression)phiNode.Value; var oldVariable = phiExpression.Variables[argumentIndex].Variable; var newVariable = stack[oldVariable].Peek(); phiExpression.Variables[argumentIndex].Variable = newVariable; } } foreach (var child in unit.DominatorTree.Nodes[n.Name].GetSuccessors()) { Rename(unit.ControlFlowGraph.Nodes[child.Name]); } // We are done with the newly introduced variables. // Pop all new versions of the variable from their stacks. foreach (var entry in originalVars) { stack[entry.Value].Pop(); } } }
public ILTransformEventArgs(ILCompilationUnit unit, IILAstTransform transform, int iteration) { Unit = unit; Transform = transform; Iteration = iteration; }
public string VisitCompilationUnit(ILCompilationUnit unit) => "unit";
private bool TryOptimizeToGreaterThan( ILCompilationUnit unit, ILFlagsVariable fl, ILVariableExpression flUsage, ILExpression flAssignment, MatchResult match) { var root = flUsage.Parent?.Parent?.Parent?.Parent; var relationalMatch = BigRelationalPattern.Match(root); if (!relationalMatch.Success || !ValidateRelationalMatch(relationalMatch, out var variable)) { return(false); } if (!ValidateRemainingRelationalNodes(match, variable, VMFlags.OVERFLOW | VMFlags.SIGN | VMFlags.ZERO, out var varAssignment, out var flAssignment2, out var cmpMatch)) { return(false); } // We have a match! Decide which opcode to use based on the original comparison that was made. ILOpCode opcode; switch (((ILInstructionExpression)flAssignment2).OpCode.Code) { case ILCode.CMP_R32: opcode = ILOpCodes.__GT_R64; break; case ILCode.CMP_R64: opcode = ILOpCodes.__GT_R64; break; case ILCode.CMP_DWORD: opcode = ILOpCodes.__GT_DWORD; break; case ILCode.CMP_QWORD: opcode = ILOpCodes.__GT_QWORD; break; default: return(false); } // Introduce new variable for storing the result of the comparison. var resultVar = unit.GetOrCreateVariable("simplified_" + fl.Name); resultVar.VariableType = VMType.Dword; var newAssignment = new ILAssignmentStatement(resultVar, new ILInstructionExpression(-1, opcode, null, VMType.Dword) { Arguments = { (ILExpression)cmpMatch.Captures["left"][0].Remove(), (ILExpression)cmpMatch.Captures["right"][0].Remove() } }); // Remove var0 assignment. varAssignment.Variable = null; varAssignment.Remove(); // Remove comparison. flAssignment2.Parent.Remove(); // Clear references to variables. var referencesToRemove = new List <ILVariableExpression>(); referencesToRemove.AddRange(root.AcceptVisitor(VariableUsageCollector.Instance)); referencesToRemove.AddRange(flAssignment.AcceptVisitor(VariableUsageCollector.Instance)); referencesToRemove.AddRange(varAssignment.AcceptVisitor(VariableUsageCollector.Instance)); referencesToRemove.AddRange(flAssignment2.AcceptVisitor(VariableUsageCollector.Instance)); foreach (var reference in referencesToRemove) { reference.Variable = null; reference.Remove(); } // Replace assignment and use of FL with new variable. flAssignment.FlagsVariable = null; flAssignment.Parent.ReplaceWith(newAssignment); root.ReplaceWith(new ILVariableExpression(resultVar)); return(true); }
public void ApplyTransformation(ILCompilationUnit unit, ILogger logger) { RemoveSPAssignments(unit); DetermineAndDeclareLocals(unit); ReplaceRawLocalReferences(unit, logger); }