Могу ли я создать рекурсивное оптимизированное выражение для хвостового вызова? - PullRequest
5 голосов
/ 26 октября 2011

Я пытаюсь построить хвостовую рекурсивную Expression в .NET 4.0.

Я могу построить его, но этот скомпилированный метод не оптимизирован с помощью хвостового вызова, несмотря на указание tailCall = true, сгенерированный IL не имеет префиксной инструкции tail..

Подскажите пожалуйста как построить рекурсив оптимизированный хвостовым вызовом Expression?

Выражение сборки ниже.

using System;
using System.Linq.Expressions;

namespace ConsoleApplication2
{
    public delegate int RecursiveFunc(RecursiveFunc function, int acc, int n);

    internal class Program
    {
        private static void Main()
        {
            var funcParam = Expression.Parameter(typeof (RecursiveFunc));
            var accParam = Expression.Parameter(typeof (int));
            var nParam = Expression.Parameter(typeof (int));
            var constZero = Expression.Constant(0, typeof (int));

            var accumExpr = Expression.Add(accParam, nParam);
            var decrimentExpr = Expression.Decrement(nParam);

            var invokeExpr = Expression.Invoke(funcParam, funcParam, 
                accumExpr, decrimentExpr);

            var testExpr = Expression.Equal(nParam, constZero);
            var condExpr = Expression.Condition(testExpr, accParam, 
                invokeExpr);

            var lambda = Expression.Lambda<RecursiveFunc>(condExpr, 
                "TailCall", true, new[] {funcParam, accParam, nParam});

            var sumParam = Expression.Parameter(typeof (RecursiveFunc), 
                "Sum");

            var method = lambda.Compile();

            var ans = method(method, 0, 100);
            Console.WriteLine(ans);
        }
    }
}

И этот сгенерированный лямбда-выражением IL ниже

.method public static int32 EvaluateTarget (
    class [ConsoleApplication2]ConsoleApplication2.RecursiveFunc '',
    int32 '',
    int32 ''
) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 25 (0x19)
    .maxstack 7

    IL_0000: ldarg.2
    IL_0001: ldc.i4.0
    IL_0002: ceq
    IL_0004: brfalse IL_000b

    IL_0009: ldarg.1
    IL_000a: ret

    IL_000b: ldarg.0
    IL_000c: ldarg.0
    IL_000d: ldarg.1
    IL_000e: ldarg.2
    IL_000f: add
    IL_0010: ldarg.2
    IL_0011: ldc.i4.1
    IL_0012: sub
    IL_0013: callvirt instance int32   
        [ConsoleApplication2]ConsoleApplication2.RecursiveFunc::Invoke(class 
        [ConsoleApplication2]ConsoleApplication2.RecursiveFunc, int32, int32)
    IL_0018: ret
} // end of method AutoGeneratedType::EvaluateTarget

1 Ответ

3 голосов
/ 24 декабря 2011

Вокруг следующих работ:

using System;
using System.Linq.Expressions;
using System.Reflection.Emit;

namespace ConsoleApplication2
{
    public delegate int RecursiveFunc(RecursiveFunc doCall, RecursiveFunc function, int acc, int n);

    internal class Program
    {
        private static void Main()
        {
            DynamicMethod dm = new DynamicMethod("DoInvokeWithTailCall", typeof(int), new Type[] { typeof(RecursiveFunc), typeof(RecursiveFunc), typeof(int), typeof(int) }, typeof(Program).Module);
            ILGenerator il = dm.GetILGenerator();
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_2);
            il.Emit(OpCodes.Ldarg_3);
            il.Emit(OpCodes.Tailcall);
            il.EmitCall(OpCodes.Callvirt, typeof(RecursiveFunc).GetMethod("Invoke"), null);
            il.Emit(OpCodes.Ret);
            RecursiveFunc doCall = (RecursiveFunc)dm.CreateDelegate(typeof(RecursiveFunc));

            var doCallParam = Expression.Parameter(typeof(RecursiveFunc));
            var funcParam = Expression.Parameter(typeof(RecursiveFunc));
            var accParam = Expression.Parameter(typeof(int));
            var nParam = Expression.Parameter(typeof(int));
            var constZero = Expression.Constant(0, typeof(int));

            var accumExpr = Expression.Add(accParam, nParam);
            var decrimentExpr = Expression.Decrement(nParam);

            //var invokeExpr = Expression.Invoke(funcParam, funcParam, funcParam, accumExpr, decrimentExpr); 
            var invokeExpr = Expression.Call(dm, doCallParam, funcParam, accumExpr, decrimentExpr);

            var testExpr = Expression.Equal(nParam, constZero);
            var condExpr = Expression.Condition(testExpr, accParam,
                invokeExpr);

            var lambda = Expression.Lambda<RecursiveFunc>(condExpr,
                "TailCall", true, new[] { doCallParam, funcParam, accParam, nParam });

            var method = lambda.Compile();

            var ans = method(doCall, method, 0, 100);
            Console.WriteLine(ans);
        }
    }
}
...