Description
Currently in MIR function calls look like this:
destination = function(...) -> [...]
where destination is a lvalue of some sort. However this assignment is a little bit troublesome where under-the-covers we need to copy the return value and zero-fill the arguments after the call finishes.
LLVM has two kind of cals invoke
and call
. call
is a regular instruction, whereas invoke
is a basic block terminator. There’s no problem with the call
instruction, because we can always produce a bunch of post-call code after call
and then simply br
into the destination block. invoke
, however, does not give us that kind of freedom and we must branch into something right after the call. (remember, we still must to copy the return value and zero-fill the arguments!).
Previously we used to generate an intermediate block and translate the copy into that, but it is problematic and gets even more-so with zeroing considered. Lately we’ve moved to translating drops/copies straight into the target block (the at_start
approach) – it turns out this is wrong in its current form, because the target block can easily have more than one predecessor!
The options considered and not considered are:
-
Pre-trans pass to add empty blocks for all invokes like that and just use
at_start
approach;- this is pretty clean in a sense that it is fully contained within trans and we get to see whole mirmap so we can do the transformation (something that’s not possible generating blocks during trans);
- lets us keep the
at_start
approach which is the cleanest one I’ve thought up so far; but - generating blocks correctly would be not-really-trivial.
-
@nikomatsakis had proposed having function return value as an rvalue which must appear inside the first statement in target block. I.e. something like
bb1: function(...) -> [bb2] bb2: destination = funcret
This seems pretty clean, and we could also zero-out arguments as a part of
funcret
, but there’s a few issues with this approach that come to mind:- funcret cannot be removed as part of optimisations (special case in optimisation passes);
- we’d have to somehow carry the state around trans?
That’s all my thoughts so far. This is pretty important to get fixed, because it causes llvm assertions for some code.