[llvm-dev] Fw: On passing structures in registers

james faure via llvm-dev llvm-dev at lists.llvm.org
Fri Sep 10 07:14:43 PDT 2021


I failed to send this to the right address, so forwarding to llvm-dev

________________________________
From: james faure <james.faure at epitech.eu>
Sent: Friday, September 10, 2021 3:55 PM
To: David Chisnall <David.Chisnall at cl.cam.ac.uk>
Subject: Re: [llvm-dev] On passing structures in registers

> copy what clang does
To summarize clang then:

  *   convert returned structs <= 128bits to { i64 , i64 }
  *   larger structs are written to "sret" pointer parameter
  *   explode struct arguments to two %i64 parameters if <= 128 bits
  *   larger structs passed by "byval" pointer

I've also run some tests with __regcall, when clang appears to be happy to return structs as large as 6 x 64 bits by value and for arguments prefers to explode structs (not even packing the fields into 2 i64s as it does without __regcall) into max 5 function parameters (after which it resorts to byval again)

The situation being so messy, and me not wanting to over specify anything instantly, is there some drawback to passing all structs by value, assuming I don't care about C interop ? I reason that extra fields would spill to the stack and thus be no worse than writing to memory explicitly.

James

________________________________
From: llvm-dev <llvm-dev-bounces at lists.llvm.org> on behalf of David Chisnall via llvm-dev <llvm-dev at lists.llvm.org>
Sent: Friday, September 10, 2021 3:16 PM
To: llvm-dev at lists.llvm.org <llvm-dev at lists.llvm.org>
Subject: Re: [llvm-dev] On passing structures in registers

On 10/09/2021 14:03, james faure via llvm-dev wrote:
> There are several ways to pass structures in and out of functions, and
> the fastest will presumably usually be in registers.
>
> Return:
> System V lets us use 2 64-bit registers for structure returns

SysV does not.  A psABI supplement to SysV does.  I guess that you're
talking about the x86-64 psABI here?

, where
> figuring this out seems to be the frontend's problem; If the packed
> struct is <= 128 bits, pass it by value as a llvm aggregate (and use
> extractvalue and insertvalue to manipulate it) otherwise write to a
> pointer with "sret" attribute and return void. It seems that once I
> commit to the sret style, llvm can no longer return the struct in
> registers, but I am also suspicious of always returning structs by
> value, and even recall reading somewhere that llvm is not good at
> handling large aggregates (maybe that applies mainly to loads and stores).
>
> Struct arguments:
> Here too we have a choice between passing a llvm aggregate or passing
> down a pointer argument with "byval" attribute. As before it is unclear
> which should be preferred.

It is unclear.  This is, unfortunately, a known problem with LLVM.
There is an implicit contract between the back end and front end on how
ABI-specific information is lowered and this often impacts mid-level
optimisers.  For example, if you want to return a structure of two
32-bit values (for example, a pair of pointers) in registers on x86 (as
the BSD / macOS 32-bit ABIs do, but the Linux 32-bit ABI does not,
though I think Linux does this for _Complex(int)) then this contract
says that you should return them in an i64.  This then causes problems
for alias analysis because the ptrtoint / inttoptr pair are treated as
escaping.

The best advice (which, I admit, is very bad) is to copy whatever clang
does.

Various people have discussed adding an ABI library or a better way of
expressing ABI constraints (e.g. function / parameter attributes
requiring specific registers / stack locations) into the IR.  If you
wanted to work on this, I personally would be incredibly happy, but it's
a fairly large amount of work.

> I'm also curious about how best to represent bitvectors, and have
> noticed clang sometimes casting structures to arrays; I currently use
> raw arrays of %i1 and extract|insert value.

In general, avoid using anything other than i{N*8} as an in-memory
representation.  i1 is typically legalised to i8 at some point in the
back-end lowering, so if you want to guarantee that something is a
single bit in memory then you should use an array of i8s and masking
operations.  The back end will infer bitfield insert / extract
instructions where available.

> My final concern is that the convention needs to be predictable, both
> for ABI compatibility reasons and to inform appropriate Bitcasting of
> function pointers. I would also like to understand what exactly informs
> the threshold for switching strategy.

I completely agree that the convention should be predictable.
Unfortunately, there is currently little consistency in LLVM over how
these implicit contracts between back and front-end are expressed.

The root cause of a lot of these problems is that LLVM occasionally
pretends that the contents of memory is typed, but does not do so very
well.  The opaque pointer work is slowly fixing the most obvious
problems here.

David
_______________________________________________
LLVM Developers mailing list
llvm-dev at lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210910/30b5232b/attachment.html>


More information about the llvm-dev mailing list