I am interested in support for multiple return paths from function calls. Such a feature is particularly useful for deterministic exception handling or to encode unboxed sum type return values (e.g. Result from Rust). To support that use case it is sufficient to support one fast regular and one (possibly slightly slower) exceptional return path. More than two return paths are probably not necessary or could be handled by the caller. Right now LLVM provides the invoke mechanism which compiles to C+±style unwinding or setjmp/longjmp. Both mechanism need runtime support and introduce a steep performance cliff.
There is the C proposal “Zero-overhead deterministic failure” by Niall Douglas, which proposes an extension to the C language where functions can have an additional failure return path, which can possibly be encoded in the carry bit. A successful function call returns with the carry bit clear and a failing function returns with the carry bit set. Then each call to such a function must be followed by a jump-if-carry-set to the exceptional code. As far as I know, the sbcl common lisp compiler uses this exact mechanism to distinguish between single-value and multi-value returns. I think they had good results using that mechanism, but I am not aware of any results regarding the “Zero-overhead deterministic failure” proposal.
I think there was a short discussion on the llvm-dev mailing list before (but I am not finding it right now), where something like return-with-carry has been considered for swift exceptions.
There is also the option to pass the exceptional return address as a separate argument (ideally via an unused register), using the call/ret mechanism for the normal path and a jump for the exceptional path. The jump will be slower since it cannot take advantage of the return address predictor.
It seems there are three possibilities to support such multi-path return mechanisms in LLVM:
- Add an additional invoke mechanism
- Add additional calling conventions
- Generalize the callbr instruction (which is currently only used for asm-goto)
Is there interest in adding something like this to LLVM? And do you have insights on what would the least-intrusive/best way to add support for this?