Invalid IL generated by Emit method?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Gianluca

    #1

    Invalid IL generated by Emit method?

    This is the simplified code I'm trying to generate:

    ilg.DeclareLoca l(typeof(int)); // define local integer
    Label exit = ilg.DefineLabel (); // define "exit" label

    ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
    ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0

    ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the stack
    ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
    ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar equal,
    jump to "exit"
    ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
    ilg.MarkLabel(e xit); // : Exit
    ilg.Emit(OpCode s.Ret); // return

    This is stripped down version of something that makes more sense than this.
    It's a simple "public object GetValue(object value)" function and according
    to the IL above it will simply return the first argument since 0 and 1 are
    not equal, the branch is not executed and the first argument is loaded on
    the stack and returned to the caller.

    When executing this function I get a "Common Language Runtime detected an
    invalid program." system exception. Now, if you move the label to another
    place it works fine:

    ilg.DeclareLoca l(typeof(int)); // define local integer
    Label exit = ilg.DefineLabel (); // define "exit" label

    ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
    ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0


    ilg.MarkLabel(e xit); // : Exit
    ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the stack
    ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
    ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar equal,
    jump to "exit"
    ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
    ilg.Emit(OpCode s.Ret); // return

    As you can see the lable has been move before the comparison and if the Beq
    is executed it would end up in an infinite loop. BUt since 0 and 1 are not
    equal the sample code above works perfectly fine.

    Anyone has an explanation for this other than a bug somewhere in the JIT,
    Emit method or something else?


  • Sherif El-Metainy

    #2
    Re: Invalid IL generated by Emit method?

    Hello

    The problem is that in case of the branch is executed, there will be nothing
    in the stack at the return statement, if the branch is not executed there
    will be arg1 will be in the stack. The JIT compiler makes sure at that all
    paths that lead to any instruction will have the same state of the stack.

    Best regards,
    Sherif El-Metainy

    "Gianluca" <gianluca@nospa m.com> wrote in message
    news:Purhe.1481 $5m1.1439@trndd c06...[color=blue]
    > This is the simplified code I'm trying to generate:
    >
    > ilg.DeclareLoca l(typeof(int)); // define local integer
    > Label exit = ilg.DefineLabel (); // define "exit" label
    >
    > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
    > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
    >
    > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the stack
    > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
    > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
    > equal,
    > jump to "exit"
    > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
    > ilg.MarkLabel(e xit); // : Exit
    > ilg.Emit(OpCode s.Ret); // return
    >
    > This is stripped down version of something that makes more sense than
    > this.
    > It's a simple "public object GetValue(object value)" function and
    > according
    > to the IL above it will simply return the first argument since 0 and 1 are
    > not equal, the branch is not executed and the first argument is loaded on
    > the stack and returned to the caller.
    >
    > When executing this function I get a "Common Language Runtime detected an
    > invalid program." system exception. Now, if you move the label to another
    > place it works fine:
    >
    > ilg.DeclareLoca l(typeof(int)); // define local integer
    > Label exit = ilg.DefineLabel (); // define "exit" label
    >
    > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
    > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
    >
    >
    > ilg.MarkLabel(e xit); // : Exit
    > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the stack
    > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
    > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
    > equal,
    > jump to "exit"
    > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
    > ilg.Emit(OpCode s.Ret); // return
    >
    > As you can see the lable has been move before the comparison and if the
    > Beq
    > is executed it would end up in an infinite loop. BUt since 0 and 1 are not
    > equal the sample code above works perfectly fine.
    >
    > Anyone has an explanation for this other than a bug somewhere in the JIT,
    > Emit method or something else?
    >
    >[/color]


    Comment

    • Gianluca

      #3
      Re: Invalid IL generated by Emit method?

      Thanks! I was going nuts over this. I was trying to load each element of an
      object[] array on the stack and redirect the call to a "wrapped" function
      and could not explain why seemingly correct IL code kept failing.
      Rearranging the labels worked, at times...

      I thought of some kind of restriction for branching but couldn't find any
      documentation over this. Well, thanks again.


      "Sherif El-Metainy" <elmeteny REMOVETHIS at thewayout NOSPAM dot net> wrote
      in message news:OJ9Q09RWFH A.3676@TK2MSFTN GP10.phx.gbl...[color=blue]
      > Hello
      >
      > The problem is that in case of the branch is executed, there will be[/color]
      nothing[color=blue]
      > in the stack at the return statement, if the branch is not executed there
      > will be arg1 will be in the stack. The JIT compiler makes sure at that all
      > paths that lead to any instruction will have the same state of the stack.
      >
      > Best regards,
      > Sherif El-Metainy
      >
      > "Gianluca" <gianluca@nospa m.com> wrote in message
      > news:Purhe.1481 $5m1.1439@trndd c06...[color=green]
      > > This is the simplified code I'm trying to generate:
      > >
      > > ilg.DeclareLoca l(typeof(int)); // define local integer
      > > Label exit = ilg.DefineLabel (); // define "exit" label
      > >
      > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
      > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
      > >
      > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color][/color]
      stack[color=blue][color=green]
      > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
      > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
      > > equal,
      > > jump to "exit"
      > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
      > > ilg.MarkLabel(e xit); // : Exit
      > > ilg.Emit(OpCode s.Ret); // return
      > >
      > > This is stripped down version of something that makes more sense than
      > > this.
      > > It's a simple "public object GetValue(object value)" function and
      > > according
      > > to the IL above it will simply return the first argument since 0 and 1[/color][/color]
      are[color=blue][color=green]
      > > not equal, the branch is not executed and the first argument is loaded[/color][/color]
      on[color=blue][color=green]
      > > the stack and returned to the caller.
      > >
      > > When executing this function I get a "Common Language Runtime detected[/color][/color]
      an[color=blue][color=green]
      > > invalid program." system exception. Now, if you move the label to[/color][/color]
      another[color=blue][color=green]
      > > place it works fine:
      > >
      > > ilg.DeclareLoca l(typeof(int)); // define local integer
      > > Label exit = ilg.DefineLabel (); // define "exit" label
      > >
      > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
      > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
      > >
      > >
      > > ilg.MarkLabel(e xit); // : Exit
      > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color][/color]
      stack[color=blue][color=green]
      > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
      > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
      > > equal,
      > > jump to "exit"
      > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
      > > ilg.Emit(OpCode s.Ret); // return
      > >
      > > As you can see the lable has been move before the comparison and if the
      > > Beq
      > > is executed it would end up in an infinite loop. BUt since 0 and 1 are[/color][/color]
      not[color=blue][color=green]
      > > equal the sample code above works perfectly fine.
      > >
      > > Anyone has an explanation for this other than a bug somewhere in the[/color][/color]
      JIT,[color=blue][color=green]
      > > Emit method or something else?
      > >
      > >[/color]
      >
      >[/color]


      Comment

      • Gianluca

        #4
        Re: Invalid IL generated by Emit method?

        ops, but this makes it impossible to write a dynamic binder class. I'm
        writing a dynamic IL generator for speeding up reflection. It works very
        well for accessing fields and it extremely faster than reflection. But I
        have a problem now when generating IL for calling methods. I have a
        generating method called "Invoke" on an interface which takes the arguments
        as object[] args. Just like MethodInfo.Invo ke(). The IL code should "unpack"
        the arguments and place them on the evaluation stack and then call the
        reflected method. The end result would be prefectly valid stack state but
        the JIT doesn't a loop that loads the stack dynamically because at the first
        junction it detects an invalid stack state. Is there a way to turn this
        thing off?

        arg_2 is object[] args

        il.DeclareLocal (typeof(int));
        // i = args.Length
        il.Emit(OpCodes .Ldarg_2);
        il.Emit(OpCodes .Ldlen);
        il.Emit(OpCodes .Conv_I4);
        il.Emit(OpCodes .Stloc_0);
        // :loop
        il.MarkLabel(lo op);
        // i = i - 1
        il.Emit(OpCodes .Ldloc_0);
        il.Emit(OpCodes .Ldc_I4_1);
        il.Emit(OpCodes .Sub);
        il.Emit(OpCodes .Stloc_0);
        // if i < 0 then exit
        il.Emit(OpCodes .Ldloc_0);
        il.Emit(OpCodes .Ldc_I4_0);
        il.Emit(OpCodes .Blt_S, exit);
        // push args[i]
        il.Emit(OpCodes .Ldarg_2);
        il.Emit(OpCodes .Ldloc_0);
        il.Emit(OpCodes .Ldelem_Ref);
        // loop
        il.Emit(OpCodes .Br_S, loop);
        // :exit
        il.MarkLabel(ex it);
        // call
        ils.EmitCall(Op Codes.Call, method, null);

        At the call the stack could be empty or could contain the unpacked
        arguments. Any idea on how to turn off the stack state restriction?


        "Gianluca" <gianluca@nospa m.com> wrote in message
        news:9bKhe.2192 $5m1.37@trnddc0 6...[color=blue]
        > Thanks! I was going nuts over this. I was trying to load each element of[/color]
        an[color=blue]
        > object[] array on the stack and redirect the call to a "wrapped" function
        > and could not explain why seemingly correct IL code kept failing.
        > Rearranging the labels worked, at times...
        >
        > I thought of some kind of restriction for branching but couldn't find any
        > documentation over this. Well, thanks again.
        >
        >
        > "Sherif El-Metainy" <elmeteny REMOVETHIS at thewayout NOSPAM dot net>[/color]
        wrote[color=blue]
        > in message news:OJ9Q09RWFH A.3676@TK2MSFTN GP10.phx.gbl...[color=green]
        > > Hello
        > >
        > > The problem is that in case of the branch is executed, there will be[/color]
        > nothing[color=green]
        > > in the stack at the return statement, if the branch is not executed[/color][/color]
        there[color=blue][color=green]
        > > will be arg1 will be in the stack. The JIT compiler makes sure at that[/color][/color]
        all[color=blue][color=green]
        > > paths that lead to any instruction will have the same state of the[/color][/color]
        stack.[color=blue][color=green]
        > >
        > > Best regards,
        > > Sherif El-Metainy
        > >
        > > "Gianluca" <gianluca@nospa m.com> wrote in message
        > > news:Purhe.1481 $5m1.1439@trndd c06...[color=darkred]
        > > > This is the simplified code I'm trying to generate:
        > > >
        > > > ilg.DeclareLoca l(typeof(int)); // define local integer
        > > > Label exit = ilg.DefineLabel (); // define "exit" label
        > > >
        > > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
        > > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
        > > >
        > > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color][/color]
        > stack[color=green][color=darkred]
        > > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
        > > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
        > > > equal,
        > > > jump to "exit"
        > > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
        > > > ilg.MarkLabel(e xit); // : Exit
        > > > ilg.Emit(OpCode s.Ret); // return
        > > >
        > > > This is stripped down version of something that makes more sense than
        > > > this.
        > > > It's a simple "public object GetValue(object value)" function and
        > > > according
        > > > to the IL above it will simply return the first argument since 0 and 1[/color][/color]
        > are[color=green][color=darkred]
        > > > not equal, the branch is not executed and the first argument is loaded[/color][/color]
        > on[color=green][color=darkred]
        > > > the stack and returned to the caller.
        > > >
        > > > When executing this function I get a "Common Language Runtime detected[/color][/color]
        > an[color=green][color=darkred]
        > > > invalid program." system exception. Now, if you move the label to[/color][/color]
        > another[color=green][color=darkred]
        > > > place it works fine:
        > > >
        > > > ilg.DeclareLoca l(typeof(int)); // define local integer
        > > > Label exit = ilg.DefineLabel (); // define "exit" label
        > > >
        > > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
        > > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
        > > >
        > > >
        > > > ilg.MarkLabel(e xit); // : Exit
        > > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color][/color]
        > stack[color=green][color=darkred]
        > > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
        > > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
        > > > equal,
        > > > jump to "exit"
        > > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the stack
        > > > ilg.Emit(OpCode s.Ret); // return
        > > >
        > > > As you can see the lable has been move before the comparison and if[/color][/color][/color]
        the[color=blue][color=green][color=darkred]
        > > > Beq
        > > > is executed it would end up in an infinite loop. BUt since 0 and 1 are[/color][/color]
        > not[color=green][color=darkred]
        > > > equal the sample code above works perfectly fine.
        > > >
        > > > Anyone has an explanation for this other than a bug somewhere in the[/color][/color]
        > JIT,[color=green][color=darkred]
        > > > Emit method or something else?
        > > >
        > > >[/color]
        > >
        > >[/color]
        >
        >[/color]


        Comment

        • Gianluca

          #5
          Re: Invalid IL generated by Emit method?

          ok, the solution is to generate the correct number of load operations for
          the actual method being wrapped.

          "Gianluca" <gianluca@nospa m.com> wrote in message
          news:3AKhe.7828 $Y12.4069@trndd c09...[color=blue]
          > ops, but this makes it impossible to write a dynamic binder class. I'm
          > writing a dynamic IL generator for speeding up reflection. It works very
          > well for accessing fields and it extremely faster than reflection. But I
          > have a problem now when generating IL for calling methods. I have a
          > generating method called "Invoke" on an interface which takes the[/color]
          arguments[color=blue]
          > as object[] args. Just like MethodInfo.Invo ke(). The IL code should[/color]
          "unpack"[color=blue]
          > the arguments and place them on the evaluation stack and then call the
          > reflected method. The end result would be prefectly valid stack state but
          > the JIT doesn't a loop that loads the stack dynamically because at the[/color]
          first[color=blue]
          > junction it detects an invalid stack state. Is there a way to turn this
          > thing off?
          >
          > arg_2 is object[] args
          >
          > il.DeclareLocal (typeof(int));
          > // i = args.Length
          > il.Emit(OpCodes .Ldarg_2);
          > il.Emit(OpCodes .Ldlen);
          > il.Emit(OpCodes .Conv_I4);
          > il.Emit(OpCodes .Stloc_0);
          > // :loop
          > il.MarkLabel(lo op);
          > // i = i - 1
          > il.Emit(OpCodes .Ldloc_0);
          > il.Emit(OpCodes .Ldc_I4_1);
          > il.Emit(OpCodes .Sub);
          > il.Emit(OpCodes .Stloc_0);
          > // if i < 0 then exit
          > il.Emit(OpCodes .Ldloc_0);
          > il.Emit(OpCodes .Ldc_I4_0);
          > il.Emit(OpCodes .Blt_S, exit);
          > // push args[i]
          > il.Emit(OpCodes .Ldarg_2);
          > il.Emit(OpCodes .Ldloc_0);
          > il.Emit(OpCodes .Ldelem_Ref);
          > // loop
          > il.Emit(OpCodes .Br_S, loop);
          > // :exit
          > il.MarkLabel(ex it);
          > // call
          > ils.EmitCall(Op Codes.Call, method, null);
          >
          > At the call the stack could be empty or could contain the unpacked
          > arguments. Any idea on how to turn off the stack state restriction?
          >
          >
          > "Gianluca" <gianluca@nospa m.com> wrote in message
          > news:9bKhe.2192 $5m1.37@trnddc0 6...[color=green]
          > > Thanks! I was going nuts over this. I was trying to load each element of[/color]
          > an[color=green]
          > > object[] array on the stack and redirect the call to a "wrapped"[/color][/color]
          function[color=blue][color=green]
          > > and could not explain why seemingly correct IL code kept failing.
          > > Rearranging the labels worked, at times...
          > >
          > > I thought of some kind of restriction for branching but couldn't find[/color][/color]
          any[color=blue][color=green]
          > > documentation over this. Well, thanks again.
          > >
          > >
          > > "Sherif El-Metainy" <elmeteny REMOVETHIS at thewayout NOSPAM dot net>[/color]
          > wrote[color=green]
          > > in message news:OJ9Q09RWFH A.3676@TK2MSFTN GP10.phx.gbl...[color=darkred]
          > > > Hello
          > > >
          > > > The problem is that in case of the branch is executed, there will be[/color]
          > > nothing[color=darkred]
          > > > in the stack at the return statement, if the branch is not executed[/color][/color]
          > there[color=green][color=darkred]
          > > > will be arg1 will be in the stack. The JIT compiler makes sure at that[/color][/color]
          > all[color=green][color=darkred]
          > > > paths that lead to any instruction will have the same state of the[/color][/color]
          > stack.[color=green][color=darkred]
          > > >
          > > > Best regards,
          > > > Sherif El-Metainy
          > > >
          > > > "Gianluca" <gianluca@nospa m.com> wrote in message
          > > > news:Purhe.1481 $5m1.1439@trndd c06...
          > > > > This is the simplified code I'm trying to generate:
          > > > >
          > > > > ilg.DeclareLoca l(typeof(int)); // define local integer
          > > > > Label exit = ilg.DefineLabel (); // define "exit" label
          > > > >
          > > > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
          > > > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
          > > > >
          > > > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color]
          > > stack[color=darkred]
          > > > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
          > > > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
          > > > > equal,
          > > > > jump to "exit"
          > > > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the[/color][/color][/color]
          stack[color=blue][color=green][color=darkred]
          > > > > ilg.MarkLabel(e xit); // : Exit
          > > > > ilg.Emit(OpCode s.Ret); // return
          > > > >
          > > > > This is stripped down version of something that makes more sense[/color][/color][/color]
          than[color=blue][color=green][color=darkred]
          > > > > this.
          > > > > It's a simple "public object GetValue(object value)" function and
          > > > > according
          > > > > to the IL above it will simply return the first argument since 0 and[/color][/color][/color]
          1[color=blue][color=green]
          > > are[color=darkred]
          > > > > not equal, the branch is not executed and the first argument is[/color][/color][/color]
          loaded[color=blue][color=green]
          > > on[color=darkred]
          > > > > the stack and returned to the caller.
          > > > >
          > > > > When executing this function I get a "Common Language Runtime[/color][/color][/color]
          detected[color=blue][color=green]
          > > an[color=darkred]
          > > > > invalid program." system exception. Now, if you move the label to[/color]
          > > another[color=darkred]
          > > > > place it works fine:
          > > > >
          > > > > ilg.DeclareLoca l(typeof(int)); // define local integer
          > > > > Label exit = ilg.DefineLabel (); // define "exit" label
          > > > >
          > > > > ilg.Emit(OpCode s.Ldc_I4_0); // put 0 on the stack
          > > > > ilg.Emit(OpCode s.Stloc_0); // set the local integer to 0
          > > > >
          > > > >
          > > > > ilg.MarkLabel(e xit); // : Exit
          > > > > ilg.Emit(OpCode s.Ldloc_0); // load the local integer (0) on the[/color]
          > > stack[color=darkred]
          > > > > ilg.Emit(OpCode s.Ldc_I4_1); // load integer 1 on the stack
          > > > > ilg.Emit(OpCode s.Beq, exit); // if the two values on the stack ar
          > > > > equal,
          > > > > jump to "exit"
          > > > > ilg.Emit(OpCode s.Ldarg_1); // load the first argument on the[/color][/color][/color]
          stack[color=blue][color=green][color=darkred]
          > > > > ilg.Emit(OpCode s.Ret); // return
          > > > >
          > > > > As you can see the lable has been move before the comparison and if[/color][/color]
          > the[color=green][color=darkred]
          > > > > Beq
          > > > > is executed it would end up in an infinite loop. BUt since 0 and 1[/color][/color][/color]
          are[color=blue][color=green]
          > > not[color=darkred]
          > > > > equal the sample code above works perfectly fine.
          > > > >
          > > > > Anyone has an explanation for this other than a bug somewhere in the[/color]
          > > JIT,[color=darkred]
          > > > > Emit method or something else?
          > > > >
          > > > >
          > > >
          > > >[/color]
          > >
          > >[/color]
          >
          >[/color]


          Comment

          Working...