question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

IL.Emit.Tail() produces wrong IL

See original GitHub issue

As tail. is a prefix instruction, the only valid code sequence is tail. call (or calli or callvirt) (see link).

Tail();
Calli(new StandAloneMethodSig(CallingConvention.Cdecl, typeof(void)));

produces

IL_0007: tail.
IL_0009: calli void()

instead of (the expected)

IL_0007: tail. calli void()

(the space after the . is sensitive though).

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:11 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
gfoidlcommented, Apr 9, 2019

Thanks 👍 for the quick fix!

1reaction
ltrzesniewskicommented, Apr 8, 2019

Well that was a pretty stupid oversight: debug builds insert nop instructions between lines of code. Your example compiles to something similar to the following:

IL_0008: tail.
IL_000A: calli     void ()
IL_000F: nop
IL_0010: ret

Which is incorrect because a tail call needs to be immediately followed by a ret.

Non-void calls are even worse:

IL_000B: tail.
IL_000D: calli     int32 (int32)
IL_0012: nop
IL_0013: stloc.0
IL_0014: br.s      IL_0016
IL_0016: ldloc.0
IL_0017: ret

The value goes into a local so the debugger can display it before the function returns, but that gets in the way too.

InlineIL will now validate tail calls and remove the boilerplate code, leaving just ret, but that will fail horribly if you have multiple tail calls in a single method. I’ll decide what to do with this case later. In the meantime, v1.1.1 should fix the issue reported here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - What's wrong with the IL I'm emitting in a Reflection. ...
Ideally I want to be using the CreateObject delegate because the code generating these dynamic methods is for a deserializer that should be...
Read more >
The frustrating state of tails calls in .NET · Issue #2191
From my my experience of x86 tail calls might not be as bad as x64 but it's IMHO not fast. I find that...
Read more >
Proposal: `tail return` recursive calls · Issue #2304
tail IL should be emitted even in the absence of the tail constraint. proposed syntax : tail qualifiedmethodname(parameters); // (the syntax is ...
Read more >
Sigil: Adding Some (More) Magic To IL
Sigil wraps ILGenerator, exposes much less error prone alternatives to Emit, and does immediate verification of the instruction stream. The ...
Read more >
OpCodes.Tailcall Field (System.Reflection.Emit)
Performs a postfixed method call instruction such that the current method's stack frame is removed before the actual call instruction is executed.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found