Direct Argument Passing in Clang

I am in the process of adding a new ABI Info to lib/CodeGen/TargetInfo.cpp. When classifying arguments passing conventions, for small structs, we want to pass directly. That is, in classifyArgumentType(), we return ABIArgInfo::getDirect() in these cases. However, I noticed that the structure is then passed by its members individually. So where I would expect a call of the form:

foo(struct s)

I instead find

foo(member1, member2, …)

This change in calling convention occurs in lib/CodeGen/CGCall.cpp – in each case near a comment stating that “If the coerce-to type is a first class aggregate, we flatten it” and further asserting that either way is semantically identical. I would presume this can only be true only if padding of structures is equivalent to padding and promotions of values on the stack. The test case I am looking at contains the use of the following as a parameter:

typedef struct S


char a;

char b;

char c;

} S;

As a whole, the struct will fit within at least 32 bits on the stack. When the members are separated, due to promotions, the struct will take up 3 * sizeof(int) on the stack.

I’m fairly certain that what I want to do is pass the arguments directly as stated above, but I wanted to check and see if there is a more preferred way for handling this.


Zachary Morgan

The comment in CGCall was accurate when it was written. All backends used to handle first-class LLVM aggregates (FCA) by passing the constituent elements separately. That was never written down as a rule, so the situation has evolved.

I believe ARM now does something different for homogenous floating point aggregates (HFAs). It’s still the case that if you pass an LLVM FCA, LLVM is allowed to take it apart and put it back together again, without preserving any padding bytes.

Depending on what you want, there are three things you can do:

  1. Use ABIArgInfo::getDirect() and set CanBeFlattened to false. This is probably the easiest, but most LLVM optimizations don’t handle FCAs very well. ARM does this for HFAs.

  2. Use getIndirect(/ByVal=/true) to pass the argument on the stack in memory with exactly the specified layout. There will be a bytewise copy. Do this if you want the argument in memory (never in registers) and you care about the struct layout.

  3. Implement custom expansion logic from the struct type to constituent member types. This is pretty much your only option if you want to pass unions in registers. This is what x86_64 SysV does for example. In the worst case, you can basically take a struct and split it up into pointer-sized ints, pass those, and reconstitute the struct on the other side.

Ah, recent commit: Updating our branch and setting CanBeFlattened works perfectly.


Zachary Morgan