[llvm-dev] [RFC] Granular Return Attributes

Nicolai Hähnle via llvm-dev llvm-dev at lists.llvm.org
Wed Dec 9 08:21:09 PST 2020


We may eventually find some use for this as well, though it's speculative.

This may be a good time to raise the question: How do people feel about
changing LLVM IR to allow multiple values defined by a single instruction?

That's a pretty significant change, but I think there are very good reasons
for wanting to do this. I know we'd appreciate not having the IR obfuscated
by `extractvalue`, for example. Smoothing the road of LLVM IR / MLIR
integration is another one.

I understand you may not want to get your particular problem blocked by
such a major change. Either way, it doesn't feel like the changes would be
in conflict with each other anyway.

Cheers,
Nicolai

On Wed, Dec 9, 2020 at 2:31 PM Tim Northover via llvm-dev <
llvm-dev at lists.llvm.org> wrote:

> One line proposal: I want to be able to write
>
>     declare { i8 signext, i16 zeroext } @foo()
>
> I think it's probably justifiable purely on symmetry grounds, a returned
> struct
> is really just a glorified parameter list to fit in with LLVM's
> requirement that
> a call produce a single Value, and why shouldn't you have as much control
> over
> how that parameter passing happens as you do on the call itself?
>
> But I do have a real motivating bugbear. Swifterror and the unfortunate
> fact
> that I need a second attribute like it.
>
> The swifterror attribute was implemented to support a special kind of
> function
> parameter that gets passed and then returned in a specific register,
> but functions
> can change.
>
> The "specific register" requirement is slightly odd, but shared by
> "swiftself"
> parameters and not a big problem to represent.
>
> But because we can't currently describe that final return (of a possibly
> different value), we perform an elaborate trick on the IR. Values are
> given a
> pseudo-memory location (created with a special alloca), and syntactic
> load/store
> operations to this get converted to normal vreg dataflow by a special
> SwiftErrorValueTracking class that essentially implements a subset of
> mem2reg
> behaviour during CodeGen. The final value is then magically returned in
> x21 (for
> AArch64).
>
> So in current IR you will see functions like this (with AArch64 real
> behaviour
> in comments):
>
>     define i32 @foo(i8** swifterror %loc) {
>       %errval = load i8*, i8** %loc     ; mov xErrVal, x21
>       ; Use current errval.
>       store i8* %newerr, i8** %loc      ; mov x21, xNewErr
>       [...]
>       ret i32 42 ; x0=42, x21=either incoming error, or new one if
> stored at some point.
>     }
>
> I'd like to replace them with what's really happening:
>
>     define { i32, i8* swifterror } @foo(i8* swifterror %errval) {
>       [...]
>       %ret.0 = insertvalue { i32, i8* } undef, i32 42, 0
>       %ret = insertvalue { i32, i8* } %ret.0, i8* %newerr, 1
>       ret { i32, i8* } %ret
>     }
>
> Front-ends can of course use a normal alloca to avoid explicitly
> value-tracking
> %newerr themselves and the real mem2reg will clean up the details.
>
> What about sret?
> ----------------
>
> I think in this new world we'd have to relax the restriction that sret
> functions
> must otherwise return void, certainly to support the swifterror use-case. I
> don't think this is a huge problem though.
>
> Which attributes would be allowed?
> ----------------------------------
>
> I'd propose starting small. Perhaps just allow signext and zeroext and
> convert swifterror in a separate patch. If people find uses for other
> attributes
> they can be added later.
>
> How do these combine with top-level return attributes?
> ------------------------------------------------------
>
> Struct return types don't currently allow attributes; you can't write
>
>     define { i32, i32 } signext @foo() {
>
> I think it's fine to keep that restriction and only allow attributes on
> first-level inner types of structs.
>
> We'd be essentially creating a dual to the function's parameter list, but
> it
> has to be written as a struct because call instructions can only produce
> a single Value in LLVM (at one point I toyed with a new syntax like C++'s
> "auto
> foo() -> (i32, i32)" but discarded that idea for this reason, as well as
> the
> scale of that change).
>
> Where's the code?
> -----------------
>
> I haven't implemented it yet because it's quite a big change and I wanted
> to
> make sure there weren't too many objections, and that I hadn't missed
> an unmovable blocker.
>
> Cheers.
>
> Tim.
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>


-- 
Lerne, wie die Welt wirklich ist,
aber vergiss niemals, wie sie sein sollte.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20201209/481805bc/attachment.html>


More information about the llvm-dev mailing list