private int Internal_CheckForTailRequests(Instruction i, int instructionPtr) { DynValue tail = m_ValueStack.Peek(0); if (tail.Type == DataType.TailCallRequest) { m_ValueStack.Pop(); // discard tail call request TailCallData tcd = tail.TailCallData; m_ValueStack.Push(tcd.Function); for (int ii = 0; ii < tcd.Args.Length; ii++) { m_ValueStack.Push(tcd.Args[ii]); } return(Internal_ExecCall(tcd.Args.Length, instructionPtr, tcd.ErrorHandler, tcd.Continuation, false, null, tcd.ErrorHandlerBeforeUnwind)); } else if (tail.Type == DataType.YieldRequest) { m_SavedInstructionPtr = instructionPtr; return(YIELD_SPECIAL_TRAP); } return(instructionPtr); }
/// <summary> /// Rewrite a tail recursive (but not using .NET tail recursion) /// function of the form myFunc(argument, accumulator) into a /// tail-recursive (with .NET tail recursion) form of same /// </summary> /// <typeparam name="A">The type of the argument to a simple recursive form of the function</typeparam> /// <typeparam name="R">The type of the result of the function</typeparam> /// <param name="nonTailRecursive">A function with a tail call, where /// the first argument is the argument to the non-tail-recursive /// form of the function, and the second is an accumulator for the /// result</param> /// <returns></returns> public static Func <A, R, R> Rewrite <A, R>(object self, Func <A, R, R> nonTailRecursive) { MethodInfo methodInfo = nonTailRecursive.Method; string assembly = methodInfo.DeclaringType.Assembly.Location; string assemblyExt = System.IO.Path.GetExtension(assembly); string tempFileName = System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), assemblyExt); AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(assembly); TypeDefinition typeDefinition = assemblyDefinition.MainModule.GetType(methodInfo.DeclaringType.FullName); assemblyDefinition.Name = new AssemblyNameDefinition("Rewritten", new Version(1, 0)); assemblyDefinition.MainModule.Name = System.IO.Path.GetFileName(tempFileName); typeDefinition.Name = "RewrittenType"; typeDefinition.Namespace = "RewrittenNamespace"; //MethodDefinition recursiveCall = new MethodDefinition() MethodDefinition definition = typeDefinition.Methods.Single(m => m.Name == methodInfo.Name); MethodReference methodReference = assemblyDefinition.MainModule.Import(methodInfo); ILProcessor ilProcessor = definition.Body.GetILProcessor(); definition.Body.SimplifyMacros(); TailCallData tailCallData = TryFindTailCall(definition.Body.Instructions, definition); while (tailCallData != null) { // rewrite as tail call // Step 1: Remove stloc/ldloc/br.s instructions before ret instruction for (var index = tailCallData.RetIndex - 1; index > tailCallData.CallIndex; index--) { ilProcessor.Remove(definition.Body.Instructions[index]); } // Step 2: Rewrite any stloc/br "returns" as proper rets foreach (var otherExit in tailCallData.OtherExits) { ilProcessor.Replace(otherExit.BrInstruction, Instruction.Create(OpCodes.Ret)); ilProcessor.Remove(otherExit.StlocInstruction); } // Step 3: Insert "tail" IL instruction before call ilProcessor.InsertBefore(tailCallData.CallInstruction, ilProcessor.Create(OpCodes.Tail)); // any more? tailCallData = TryFindTailCall(definition.Body.Instructions, definition); } definition.Body.OptimizeMacros(); assemblyDefinition.Write(tempFileName); Assembly rewrittenAssembly = Assembly.LoadFile(tempFileName); Type rewrittenType = rewrittenAssembly.GetType(definition.DeclaringType.FullName); MethodInfo rewrittenMethod = rewrittenType.GetMethod( definition.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, null, new[] { typeof(A), typeof(R) }, null); Expression instance = self != null?Expression.Constant(self) : null; Func <A, R, R> result = (Func <A, R, R>)Delegate.CreateDelegate(typeof(Func <A, R, R>), rewrittenMethod); return(result); }
private static TailCallData TryFindTailCall(Mono.Collections.Generic.Collection <Instruction> instructions, MethodDefinition method) { foreach (var item in instructions.Select((instruction, index) => new { instruction, index })) { TailCallData tailCallData = TryGetTailCallData(item.index, method); if (tailCallData != null) { return(tailCallData); } } return(null); }