private CFGNode ProcessCFGNode( CFGNode calleeNode, CFGNode callerNode, CFGNode callerSuccessorNode, IExpression thisReplacement, IExpression returnValueTarget, MethodCompileInfo calleeMethodCompileInfo, MethodCompileInfo callerMethodCompileInfo, Dictionary<string, VariableDefinition> renamedLocalsMap, Dictionary<string, VariableReference> variableReplacements, Dictionary<string, IExpression> argumentReplacements, Dictionary<CFGNode, CFGNode> processedNodes, CFGNodeSet newNodes ) { CFGNode inlinedNode; if (processedNodes.TryGetValue(calleeNode, out inlinedNode)) { // we've already seen this node // don't inline it again otherwise we'll end up in an infinite recursion return inlinedNode; } CFG cfg = callerNode.Graph; inlinedNode = cfg.AddNode(); inlinedNode.FlowControl = calleeNode.FlowControl; inlinedNode.BasicBlock = CodeUtility.CloneCode<BasicBlock>(calleeNode.BasicBlock); processedNodes[calleeNode] = inlinedNode; newNodes.Add(inlinedNode); Debug.Assert( inlinedNode.BasicBlock.Statements.Count == calleeNode.BasicBlock.Statements.Count, string.Format("Clone code produced {0} statements instead of {1}", inlinedNode.BasicBlock.Statements.Count, calleeNode.BasicBlock.Statements.Count) ); // rename all variables in callee so they don't clash with variables in caller CodeUtility.MatchAndReplaceCode<IVariableReferenceExpression>( inlinedNode.BasicBlock, delegate(IVariableReferenceExpression expression) { return true; }, delegate(IVariableReferenceExpression expression) { VariableReference variableReplacement; if (!variableReplacements.TryGetValue(expression.Variable.Name, out variableReplacement)) { if (callerMethodCompileInfo.IsTemporary(expression.Variable)) { variableReplacement = callerMethodCompileInfo.NewTemporary(expression.Variable.VariableType); } else if (callerMethodCompileInfo.IsFrozen(expression.Variable)) { variableReplacement = callerMethodCompileInfo.NewFrozen(expression.Variable.VariableType); } else { VariableDefinition variableDefinition; if (!renamedLocalsMap.TryGetValue(expression.Variable.Name, out variableDefinition)) { int index = callerMethodCompileInfo.Method.Body.Variables.Count; string name = "V_" + index; // TODO: Extract into constant and helper method TypeReference type = expression.Variable.VariableType; variableDefinition = new VariableDefinition(name, index, callerMethodCompileInfo.Method, type); callerMethodCompileInfo.Method.Body.Variables.Add(variableDefinition); renamedLocalsMap[expression.Variable.Name] = variableDefinition; } variableReplacement = variableDefinition; } variableReplacements[expression.Variable.Name] = variableReplacement; } expression.Variable = variableReplacement; return expression; }, true ); // replace all instances of "this" in callee with the target object if (thisReplacement != null) { CodeUtility.MatchAndReplaceCode<IExpression>( inlinedNode.BasicBlock, delegate(IExpression expression) { return expression is IThisReferenceExpression; }, delegate(IExpression expression) { return thisReplacement; }, true ); } // replace all arguments references if (argumentReplacements != null) { CodeUtility.MatchAndReplaceCode<IExpression>( inlinedNode.BasicBlock, delegate(IExpression expression) { return expression is IArgumentReferenceExpression; }, delegate(IExpression expression) { IArgumentReferenceExpression argumentReferenceExpression = (IArgumentReferenceExpression)expression; IExpression replacementExpression; if (!argumentReplacements.TryGetValue(argumentReferenceExpression.Parameter.Name, out replacementExpression)) { throw new CompilerException( string.Format( "Error while inlining call to {0}. Could not find argument replacement for parameter \"{1}\". Parameter sequence is {2}.", calleeMethodCompileInfo.Method, argumentReferenceExpression.Parameter.Name, argumentReferenceExpression.Parameter.Sequence ) ); } else { return replacementExpression; } }, true ); } if (inlinedNode.FlowControl == FlowControl.Throw) { // TODO: fixup exception handling info throw new CannotInlineMethodException("Contain throw statement"); } else if (inlinedNode.FlowControl == FlowControl.ConditionalBranch) { // TODO: Fix this so that conditioanl branches can be inlined throw new CannotInlineMethodException("Contains a conditional branch"); } else if (inlinedNode.FlowControl == FlowControl.Return) { // replace return statement with assignment to variable being assigned to in caller Debug.Assert(inlinedNode.BasicBlock.Statements.Count == 1, string.Format("Return basic block has {0} statements", inlinedNode.BasicBlock.Statements.Count)); Debug.Assert(inlinedNode.BasicBlock.Statements[0] is IMethodReturnStatement); IMethodReturnStatement methodReturnStatement = (IMethodReturnStatement)inlinedNode.BasicBlock.Statements[0]; inlinedNode.BasicBlock.Statements.Clear(); inlinedNode.BasicBlock.Statements.Add(new AssignStatement(returnValueTarget, methodReturnStatement.Expression)); // add edge between this return node and the caller node's successor Debug.Assert(inlinedNode.SuccessorCount == 0); Debug.Assert(callerSuccessorNode != null); cfg.AddEdge(inlinedNode, callerSuccessorNode); // flow control is no longer return inlinedNode.FlowControl = FlowControl.Next; } foreach (CFGEdge outEdge in calleeNode.Graph.OutEdges(calleeNode)) { CFGNode newSuccessorNode = ProcessCFGNode( outEdge.Target, callerNode, callerSuccessorNode, thisReplacement, returnValueTarget, calleeMethodCompileInfo, callerMethodCompileInfo, renamedLocalsMap, variableReplacements, argumentReplacements, processedNodes, newNodes ); cfg.AddEdge(inlinedNode, newSuccessorNode, outEdge.BranchCondition); } return inlinedNode; }
public override bool Transform(MethodCompileInfo callerMethodCompileInfo) { List<CFGNode> callerNodes = new List<CFGNode>(); foreach (CFGNode node in callerMethodCompileInfo.CFG.Vertices) { callerNodes.Add(node); } foreach (CFGNode callerNode in callerNodes) { BasicBlock bb = callerNode.BasicBlock; // TODO: For now assume that the only statement is an assignstatement if (bb.Statements.Count != 1) { continue; } IAssignStatement assignStatement = bb.Statements[0] as IAssignStatement; if (assignStatement == null) { continue; } IMethodInvokeExpression methodInvokeExpression = assignStatement.Expression as IMethodInvokeExpression; if (methodInvokeExpression == null) { continue; } IMethodReferenceExpression methodReferenceExpression = methodInvokeExpression.Method as IMethodReferenceExpression; if (methodReferenceExpression == null) { continue; } MethodDefinition methodDefinition; if (!ReferenceResolver.TryResolveMethodReference(methodReferenceExpression.Method, callerMethodCompileInfo.AssemblyCompileInfo.Assembly.Resolver, out methodDefinition)) { continue; } MethodCompileInfo calleeMethodCompileInfo; if (!callerMethodCompileInfo.AssemblyCompileInfo.MethodCompileInfos.TryGetValue(methodDefinition, out calleeMethodCompileInfo)) { continue; } if (callerNode.FlowControl != FlowControl.Call) { continue; } if (!CanBeInlined(calleeMethodCompileInfo, callerMethodCompileInfo)) { continue; } // graft the callee CFG into the caller CFG Debug.Assert(callerNode.SuccessorCount == 1); CFGNode successorNode = callerNode.Successors[0]; // TODO: Write GetOnlySuccessor() method CFGNode newCallerNode = callerNode.Graph.AddNode(); newCallerNode.BasicBlock.Statements.Add(new NopStatement()); CFGNodeSet newNodes = new CFGNodeSet(newCallerNode.Graph); newNodes.Add(newCallerNode); IExpression thisReplacement = calleeMethodCompileInfo.Method.IsStatic ? null : methodReferenceExpression.Target; Dictionary<string, IExpression> argumentReplacements; if (methodDefinition.Parameters.Count > 0) { argumentReplacements = new Dictionary<string, IExpression>(); foreach (ParameterDefinition parameterDefinition in methodDefinition.Parameters) { argumentReplacements[parameterDefinition.Name] = methodInvokeExpression.Arguments[parameterDefinition.Sequence - 1]; } } else { argumentReplacements = null; } try { CFGNode calleeNode = ProcessCFGNode( calleeMethodCompileInfo.CFG.Root, newCallerNode, successorNode, thisReplacement, assignStatement.Target, calleeMethodCompileInfo, callerMethodCompileInfo, new Dictionary<string, VariableDefinition>(), new Dictionary<string, VariableReference>(), argumentReplacements, new Dictionary<CFGNode, CFGNode>(), newNodes ); foreach (CFGEdge inEdge in callerNode.Graph.InEdges(callerNode)) { callerNode.Graph.AddEdge(inEdge.Source, newCallerNode, inEdge.BranchCondition); } newCallerNode.Graph.AddEdge(newCallerNode, calleeNode); newCallerNode.FlowControl = FlowControl.Next; callerNode.Graph.RemoveVertex(callerNode); //Console.WriteLine("Inlined method " + calleeMethodCompileInfo + " in method " + callerMethodCompileInfo); } catch (CannotInlineMethodException e) { //Console.WriteLine("Cannot inline method " + calleeMethodCompileInfo + " in method " + calleeMethodCompileInfo + ": " + e); // inlining failed, let's clean up any nodes we created foreach (CFGNode newNode in newNodes) { newNode.Graph.RemoveVertex(newNode); } }; } return true; }
public override bool Transform(MethodCompileInfo methodCompileInfo) { _methodCompileInfo = methodCompileInfo; CFGNodeSet wholeSet = new CFGNodeSet(_methodCompileInfo.CFG); List<CFGNode> sortedNodes = new List<CFGNode>(); CFGNodeSet roots = new CFGNodeSet(_methodCompileInfo.CFG.Root);//_compileInfo.Roots; try { QuickGraph.Algorithms.TopologicalSortAlgorithm sortAlgorithm = new QuickGraph.Algorithms.TopologicalSortAlgorithm(_methodCompileInfo.CFG); sortAlgorithm.Compute(); foreach (CFGNode node in sortAlgorithm.SortedVertices) { if (!roots.Contains(node)) { sortedNodes.Add(node); } wholeSet.Add(node); } } catch (QuickGraph.Exceptions.NonAcyclicGraphException) { foreach (QuickGraph.Concepts.IVertex vertex in _methodCompileInfo.CFG.Vertices) { if (!roots.Contains((CFGNode)vertex)) { sortedNodes.Add((CFGNode)vertex); } wholeSet.Add((CFGNode)vertex); } } Dictionary<CFGNode, CFGNodeSet> domMap = _methodCompileInfo.Dominators; domMap.Clear(); foreach (CFGNode root in roots) { // root only dominated by itself domMap[root] = new CFGNodeSet(root); } Dictionary<CFGNode, CFGNode[]> predecessorMap = new Dictionary<CFGNode, CFGNode[]>(); foreach (CFGNode node in sortedNodes) { predecessorMap[node] = node.Predecessors; } Dictionary<CFGNode, CFGNodeSet> domMapPrime = new Dictionary<CFGNode, CFGNodeSet>(); foreach (KeyValuePair<CFGNode, CFGNodeSet> entry in domMap) { domMapPrime.Add(entry.Key, entry.Value.Clone()); } // initialize to the whole set foreach (CFGNode node in sortedNodes) { domMap[node] = wholeSet; } do { bool changed = false; for (int i = 0; i < sortedNodes.Count; i++) { CFGNode n = sortedNodes[i]; domMapPrime[n] = domMap[n]; domMap[n] = new CFGNodeSet(n); if (predecessorMap[n].Length > 0) { CFGNode[] predecessors = predecessorMap[n]; CFGNodeSet predDomSet = domMap[predecessors[0]].Clone(); for (int j = 1; j < predecessors.Length; j++) { predDomSet = predDomSet & domMap[predecessors[j]]; } domMap[n] += predDomSet; } if (!changed && domMap[n] != domMapPrime[n]) { changed = true; } } if (changed) { continue; } } while (!DominatorMapsEquals(domMap, domMapPrime)); // TODO: can we find idom at the same time as we find dominator set? // find immediate dominators Dictionary<CFGNode, CFGNode> idomMap = _methodCompileInfo.ImmediateDominator; for (int i = 0; i < sortedNodes.Count; i++) { CFGNode n = sortedNodes[i]; CFGNode immediateDominator = null; foreach (CFGNode dominator in domMap[n]) { if (dominator == n) { continue; } if (immediateDominator == null) { immediateDominator = dominator; } else { if (domMap[dominator].Contains(immediateDominator)) { immediateDominator = dominator; } } } idomMap[n] = immediateDominator; } // dominator frontier Dictionary<CFGNode, CFGNodeSet> frontierMap = _methodCompileInfo.DominatorFrontier; frontierMap.Clear(); for (int i = 0; i < sortedNodes.Count; i++) { CFGNode n = sortedNodes[i]; if (predecessorMap[n].Length >= 2) { CFGNode immediateDominator = idomMap[n]; foreach (CFGNode predecessor in predecessorMap[n]) { CFGNode runner = predecessor; while (runner != immediateDominator && idomMap.ContainsKey(runner)) { CFGNodeSet frontier; if (!frontierMap.TryGetValue(runner, out frontier)) { frontier = new CFGNodeSet(_methodCompileInfo.CFG); frontierMap[runner] = frontier; } frontier.Add(n); runner = idomMap[runner]; } } } } return true; }