-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Full tailcall support on Unix #4907
Comments
The "slow" helper based tailcalls are missing - see dotnet/coreclr#703. The existing design the "slow" helper based tailcalls has a strong dependency on how varargs work on Windows (it assumes that va_list is castable to void* that does not hold on Unix), and it is super complex (requires ~500 line handwritten assembly thunk emitter). I believe the contract between JIT and VM for slow tailcalls needs to be redesigned to make the Unix implementation feasible. |
Super, thanks Jan. If possible, could you add some notes about when the slow tailcall helper is used (which AFAIU should correspond to when tailcalls will currently not be taken for CoreCLR on Unix?). It would be useful to have docs on when the slow helper is used on Windows in any case. |
This is implemented in The simple rule that should hold across platforms over time is that the fast tailcall will be used if both of the following are true:
There are additional platform-specific and implementation-specific situations where the fast tailcall will be used that can be found by consulting |
I had a rather wild thought: what about using a different calling convention (callee-pops) for tail calls? This would break lots of stuff on Windows, but as I understand it DWARF unwinding is more flexible. |
Managed code unwinding is not using DWARF on Unix. We use the same unwind info format as on Windows. Also, having a completely different calling convention for selected functions would complicate a lot of stuff for runtime (e.g. for reflection) and JIT. |
@janvorli What about using explicit frame pointers? I think that LLVM can do this too, even on Windows (source: LLVM supports tail calls on Windows, and I can't see any other way that this could be implemented). Why does the calling convention impact reflection? |
@DemiMarie the reflection prepares arguments for a function call based on the calling convention - putting register arguments into appropriate slots in a transition block structure and the others on the stack. It then transitively calls assembler helper CallDescrWorkerInternal that loads registers from the transition block, prepares stack, calls the actual method invoked via reflection and after it returns, stores its result into the CallDescrData. See RuntimeMethodHandle::InvokeMethod here: Regarding a different calling convention, there is another problem. Any method can be called via tail call in general and the same method can be called from another place using regular call. So having a different calling convention for tail calls would mean you'd have to have two jitted versions of the same function in case a function was called both ways. As for the explicit frame pointers - can you please be more specific on your idea on taking advantage of these? |
@janvorli yes, I know that some methods would be JITed twice. My hypothesis is that this would be an acceptable overhead for the use cases (as an alternative to slow tail call). Regarding explicit frame pointers, my basic idea is to figure out exactly what LLVM does, and do that. I don't know the details, but according to this source a frame pointer can be used to allow the stack pointer to be manipulated. I know (from the LLVM documentation) that LLVM can support tail calls on Windows/x64, and I strongly suspect that it supports unwinding, as otherwise debugging would be impossible. Regarding reflection: under my proposal, this would be done using the version of the method with the standard calling convention. |
We're now hitting this more prominently with F# on Unix being far more widespread. We're also going to have a .NET Core-based editing experience in VSCode and eventually VS for Mac. |
@erozenfeld we can close this now, right? |
Yes, this is fixed by #341. |
It is important to have uniform managed tailcall support accross platforms for F#.
theme:tail-call
The text was updated successfully, but these errors were encountered: