When the WebAssembly backend encounters a return type that doesn't
fit within i32, SelectionDAG performs sret demotion, adding an
additional argument to the start of the function that contains
a pointer to an sret buffer to use instead. However, this conflicts
with the emscripten sjlj lowering pass. There we translate calls like:
call {i32, i32} @foo()
into (in pseudo-llvm
%addr = @foo call {i32, i32} @__invoke_{i32,i32}(%addr)
i.e. we perform an indirect call through an extra function.
However, the sret transform now transforms this into
the equivalent of
%addr = @foo %sret = alloca {i32, i32} call {i32, i32} @__invoke_{i32,i32}(%sret, %addr)
(while simultaneously translation the implementation of @foo as well).
Unfortunately, this doesn't work out. The __invoke_ ABI expected
the function address to be the first argument, causing crashes.
There is several possible ways to fix this:
- Implementing the sret rewrite at the IR level as well and performing it as part of lowering to __invoke
- Fixing the wasm backend to recognize that __invoke has a special ABI
- A change to the binaryen/emscripten ABI to recognize this situation
This revision implements the middle option, teaching the backend to
treat __invoke_ functions specially in sret lowering. This is achieved
by
- Introducing a new CallingConv ID for invoke functions
- Adding a hook to CanLowerReturn to override which argument is used for the sret pointer.
This revision isn't quite complete and definitely needs a test
(and I'm considering dropping the hook in 2) in favor of just
swizzeling the arguments in lowerCall), but I have verified
that it indeed fixes the observed crashes, so it should be
sufficient to discuss whether this is the correct approach.