simetra
simetra

Reputation: 11

InvalidProgramException: Invalid IL code happens when emitting IL code with non-void return type

I am trying to lower down reflection method call time by creating a DynamicMethod, emitting IL code with its IL generator, and then creating a delegate using its CreateDelegate method. So far the call times are reduced significantly even though the new method call still uses (object callObject, object[] params) as parameters. The problem occurs in the generated IL code when the return type is anything other than void. I have no knowledge of MSIL and did quite a bit of research but found nothing. Here is the code that works:

private void Start()
{
    var a = new A();
    var method = typeof(A).GetMethod("Add");

    Type[] paramsTypes = { typeof(object[]) };
    Type[] allTypes = { typeof(object), typeof(object[]) };
    Type returnType = typeof(void);

    var dm = new DynamicMethod("Hi", returnType, allTypes);

    var il = dm.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Call, method, paramsTypes);
    il.Emit(OpCodes.Ret);

    var del4 = (Action<object, object[]>)dm.CreateDelegate(
        typeof(Action<object, object[]>));

    var time = DateTime.Now;

    for (int i = 0; i < 20000000; i++)
    {
        //a.Add(); 132 ms

        //method.Invoke(a, null);// 25 sec

        del4(a, null); // 200 ms
    }

    var ts = DateTime.Now - time;

    Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}

public class A
{
    public int a = 0;

    public void Add() => a++;
}

The resulting time (only the call time is measured) is so much faster compared to normal MethodInfo.Invoke. However, with the following tweaks, the IL generator throws error:

private void Start()
{
    var a = new A();
    var method = typeof(A).GetMethod("Add");

    Type[] paramsTypes = { typeof(object[]) };
    Type[] allTypes = { typeof(object), typeof(object[]) };
    Type returnType = typeof(object);

    var dm = new DynamicMethod("Hi", returnType, allTypes);

    var il = dm.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Call, method, paramsTypes);
    il.Emit(OpCodes.Ret);

    var del4 = (Func<object, object[], object>)dm.CreateDelegate(
        typeof(Func<object, object[], object>));

    var time = DateTime.Now;

    for (int i = 0; i < 20000000; i++)
    {
        //a.Add(); 132 ms

        //method.Invoke(a, null);// 25 sec

        del4(a, null); // 200 ms
    }

    var ts = DateTime.Now - time;

    Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}

public class A
{
    public int a = 0;

    public int Add() => a++;
}

When the return type expected is something other than void, the following exception is thrown:

InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:Hi (object,object[]): IL_0006: ret

Does anyone know how to solve this issue?

Edit: Added boxing before return opcode:

    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Call, method, paramsTypes);
    il.Emit(OpCodes.Box);
    il.Emit(OpCodes.Ret);

Then the following is thrown:

VerificationException: Error in System.Object:(wrapper dynamic-method) object:Hi (object,object[]) Invalid instruction 8c

Upvotes: 1

Views: 649

Answers (0)

Related Questions