JIT seems to be less aggressive in inlining with IL generated for calling DynamicMethod from another DynamicMethod. Not sure if it is expected/desirable behaviour and whether it can be easily fixed?
Simple code:
public class C {
public static int MethodA(int x) => MethodB(x, 1000);
public static int MethodB(int x, int y) => x * x + y;
}
is translated to:
.method public hidebysig static int32 MethodA (int32 x) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4 1000
IL_0006: call int32 C::MethodB(int32, int32)
IL_000b: ret
}
.method public hidebysig static int32 MethodB (int32 x, int32 y) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: mul
IL_0003: ldarg.1
IL_0004: add
IL_0005: ret
}
and then JIT produces nice result of MethodA:
C.MethodA(Int32)
L0000: mov eax, ecx
L0002: imul eax, ecx
L0005: add eax, 0x3e8
L000a: ret
while in case ILGenerator producing exactly the same IL:
private static DynamicMethod EmitMethodA(DynamicMethod secondMethod)
{
DynamicMethod code = new DynamicMethod(string.Empty, typeof(int), new[] { typeof(int) });
var ilGen = code.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldc_I4, 1000);
ilGen.EmitCall(OpCodes.Call, secondMethod, null);
ilGen.Emit(OpCodes.Ret);
return code;
}
private static DynamicMethod EmitMethodB()
{
DynamicMethod code = new DynamicMethod(string.Empty, typeof(int), new [] {typeof(int), typeof(int)});
var ilGen = code.GetILGenerator();
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Dup);
ilGen.Emit(OpCodes.Mul);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Add);
ilGen.Emit(OpCodes.Ret);
return code;
}
JIT produces non-inlined methods:
> !u 00007ff9`c50b0080
Normal JIT generated code
DynamicClass.(Int32)
Begin 00007FF9C50B0080, size 12
00007ff9`c50b0080 bae8030000 mov edx,3E8h
00007ff9`c50b0085 48b87052fbc4f97f0000 mov rax,7FF9C4FB5270h
00007ff9`c50b008f 48ffe0 jmp rax
> !u 00007ff9`c50b00f0
Normal JIT generated code
DynamicClass.(Int32, Int32)
Begin 00007FF9C50B00F0, size 8
00007ff9`c50b00f0 8bc1 mov eax,ecx
00007ff9`c50b00f2 0fafc1 imul eax,ecx
00007ff9`c50b00f5 03c2 add eax,edx
00007ff9`c50b00f7 c3 ret
JIT seems to be less aggressive in inlining with IL generated for calling
DynamicMethodfrom anotherDynamicMethod. Not sure if it is expected/desirable behaviour and whether it can be easily fixed?Simple code:
is translated to:
and then JIT produces nice result of
MethodA:while in case
ILGeneratorproducing exactly the same IL:JIT produces non-inlined methods: