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; }