Skip to content

[MIR] Reconsider invoked function return value strategy #32105

Closed
@nagisa

Description

@nagisa

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;

    1. 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);
    2. lets us keep the at_start approach which is the cleanest one I’ve thought up so far; but
    3. 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:

    1. funcret cannot be removed as part of optimisations (special case in optimisation passes);
    2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions