[llvm] [SystemZ] Handle IR struct arguments correctly. (PR #169583)

Ulrich Weigand via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 4 09:00:34 PST 2025


uweigand wrote:

>The broken (previous) test case fixed in CanLowerReturn by recognizing not only i128 arguments, but any >64 bit int argument.

Ah, good catch!
 
>Tests improved / reworked to cover hopefully all twists including incoming args, passing arg to call, saving/loading from memory and returning. Not sure if this is a little too much?

I like the tests as they are, thanks!

>See inline question about the variation as to where an i72 resides in memory in its 16 byte slot.

Replied to inline.

> > Is there any particular reason for using fastcc everywhere? This should work with the default calling convention just the same, shouldn't it?
> 
> I added it because the default "ccc" (per lang.ref) "matches the target C calling conventions", while fastcc "allows the target to use whatever tricks it wants to produce fast code for the target, without having to conform to an externally specified ABI". It's not needed for the tests, so I removed it. Maybe this then is considered the default "behavior" when the actual SystemZ ABI is broken, but with the "bug" being in the FE(?).

This is an orthogonal issue, I think.   The way to look at it is this: an ABI defines for each argument and/or return value how it is being passed in registers and/or memory, depending on its data type.  However, the set of data types is somewhat inherently language-specific, so there is really an ABI for each language.   The *platform* ABI is defined on a set of basic types (modeled after the C language), and language ABIs normally are defined in terms of the platform ABI for those cases where a language type matches one of the system ABI basic types.  But there are still cases that go beyond that; for example the C++ ABI defines how C++ classes are being passed as an extension to the platform ABI.

Now, with LLVM we really have multiple languages to be considered.  LLVM itself has LLVM IR as a "mini-language", which defines a simple set of types, which mostly but not completely matches the platform ABI.   So we need to define an ABI *for LLVM IR* that is related to but not identical to the platform ABI. For example, our platform ABI defines how to pass "signed int" or "unsigned int" by sign- or zero-extending to full GPR.  But the LLVM IR "i32" matches neither of those, and is passed without extension.  Similarly, the LLVM IR struct or array types do not map directly to C types.

So far we have: a platform ABI, and language ABIs defined in relation to platform ABI, the language ABIs including an LLVM-defined ABI for LLVM IR, and language-standard defined ABIs for C, C++, etc.   Note that since language front-ends lower language-specific calls to LLVM IR call, they need to lower language data types to LLVM IR types that have the intended effect - in effect matching the language ABI to the LLVM IR ABI.  That's why clang will have to add sext/zext markers as needed, and will have to implement indirect structure passing rules directly (and of course all the C++ class ABI etc.).

Now, as an orthogonal twist, in some cases a language defines multiple ABI *variants*.  For example, there's the "fastcall" convention defined as a variant of the C ABI as a language extension in GCC and clang.  Now, in order to implement this correctly, clang has to map this onto the LLVM IR, which wouldn't be easily possible as is with the default LLVM IR ABI.  Therefore, the LLVM IR ABI *also* defines variants like "fastcc", which then can be used by clang to efficiently implement language variant ABIs.

But this difference between variants of the C ABI is unrelated to the difference between the LLVM IR ABI and the C ABI.


https://github.com/llvm/llvm-project/pull/169583


More information about the llvm-commits mailing list