-
-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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_startapproach;- 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_startapproach 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 = funcretThis 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.