Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Have I made a mistake in this IL I'm not seeing?

I'm working on a compiler using System.Reflection.Emit, and I'm getting JIT limitation errors I can't figure out. The problem occurs in my implementation of function handles. I.e. generating the code for

function foo() { }
f = foo;
f();

Due to specifications beyond my control, the language is dynamically typed, so I can't know how many arguments f will expect at compile time. To counter this, rather than emitting a Ldftn for foo, I generate a new method, λfoo, that takes an array of the arguments given in the call expression and pushes them onto the eval stack for foo. Is that allowed in the CLR?

What I get right now is a "JIT has encountered an internal limitation" exception (or "CLR has detected an invalid program" if I save the assembly and run it instead of calling it from memory) with a stack trace showing it happens in λfoo. This is the IL I'm generating.

.method private instance class [MylibInterop]MylibInterop.MylibValue 
        'λfoo'(class [MylibInterop]MylibInterop.MylibValue[] A_1) cil managed
{
  // Code size       90 (0x5a)
  .maxstack  10
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  ldarg.1
  IL_0001:  call       instance int32 [mscorlib]System.Array::get_Length()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4     0x0
  IL_000d:  ble        IL_001d
  IL_0012:  ldstr      "Too many arguments to lambda call"
  IL_0017:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
  IL_001c:  throw
  IL_001d:  ldarg.0
  IL_001e:  ldc.i4.0
  IL_001f:  stloc.1
  IL_0020:  ldloc.0
  IL_0021:  newobj     instance void [MylibInterop]MylibInterop.MylibValue::.ctor(int32)
  IL_0026:  ldloc.1
  IL_0027:  ldloc.0
  IL_0028:  bge        IL_003d
  IL_002d:  ldarg.1
  IL_002e:  ldloc.1
  IL_002f:  ldelem     [MylibInterop]MylibInterop.MylibValue
  IL_0034:  ldloc.1
  IL_0035:  ldc.i4.1
  IL_0036:  add
  IL_0037:  stloc.1
  IL_0038:  br         IL_0026
  IL_003d:  ldloc.0
  IL_003e:  stloc.1
  IL_003f:  ldloc.1
  IL_0040:  ldc.i4     0x0
  IL_0045:  bge        IL_0054
  IL_004a:  ldnull
  IL_004b:  ldloc.1
  IL_004c:  ldc.i4.1
  IL_004d:  add
  IL_004e:  stloc.1
  IL_004f:  br         IL_003f
  IL_0054:  call       instance class [MylibInterop]MylibInterop.MylibValue debug.Program::foo(class [MylibInterop]MylibInterop.MylibValue)
  IL_0059:  ret
} // end of method Program::'λfoo'
like image 543
cthom06 Avatar asked Feb 04 '13 16:02

cthom06


1 Answers

@leppie got it in the comments: the stack needs to be deterministic; it isn't in the code I was generating (even if I know it's pushing the right number of args). I was able to get around this because the compiler had enough information to unroll the loop (hence the constants in the generated IL) and thus produce a deterministic stack.

like image 102
cthom06 Avatar answered Sep 18 '22 14:09

cthom06