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

Jonas Paulsson via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 26 08:03:37 PST 2025


================
@@ -0,0 +1,323 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z16 | FileCheck %s --check-prefix=VECTOR
+;
+; Test passing IR struct arguments, which do not adhere to the ABI but are
+; split up with each element passed like a separate argument.
+
+ at fnptr = external global ptr
+
+%Ty0 = type {i128}
+define fastcc void @fun0(%Ty0 %A) {
+; CHECK-LABEL: fun0:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r14, %r15, 112(%r15)
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -176
+; CHECK-NEXT:    .cfi_def_cfa_offset 336
+; CHECK-NEXT:    lg %r0, 8(%r2)
+; CHECK-NEXT:    lg %r1, 0(%r2)
+; CHECK-NEXT:    stg %r0, 168(%r15)
+; CHECK-NEXT:    la %r2, 160(%r15)
+; CHECK-NEXT:    stg %r1, 160(%r15)
+; CHECK-NEXT:    brasl %r14, fnptr at PLT
+; CHECK-NEXT:    lmg %r14, %r15, 288(%r15)
+; CHECK-NEXT:    br %r14
+;
+; VECTOR-LABEL: fun0:
+; VECTOR:       # %bb.0:
+; VECTOR-NEXT:    stmg %r14, %r15, 112(%r15)
+; VECTOR-NEXT:    .cfi_offset %r14, -48
+; VECTOR-NEXT:    .cfi_offset %r15, -40
+; VECTOR-NEXT:    aghi %r15, -176
+; VECTOR-NEXT:    .cfi_def_cfa_offset 336
+; VECTOR-NEXT:    vl %v0, 0(%r2), 3
+; VECTOR-NEXT:    la %r2, 160(%r15)
+; VECTOR-NEXT:    vst %v0, 160(%r15), 3
+; VECTOR-NEXT:    brasl %r14, fnptr at PLT
+; VECTOR-NEXT:    lmg %r14, %r15, 288(%r15)
+; VECTOR-NEXT:    br %r14
+  call void @fnptr(%Ty0 %A)
+  ret void
+}
+
+%Ty1 = type {i128, i128}
+define fastcc void @fun1(%Ty1 %A, %Ty1 %B) {
+; CHECK-LABEL: fun1:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r13, %r15, 104(%r15)
+; CHECK-NEXT:    .cfi_offset %r13, -56
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -224
+; CHECK-NEXT:    .cfi_def_cfa_offset 384
+; CHECK-NEXT:    lg %r0, 0(%r2)
+; CHECK-NEXT:    lg %r1, 8(%r2)
+; CHECK-NEXT:    lg %r2, 0(%r3)
+; CHECK-NEXT:    lg %r3, 8(%r3)
+; CHECK-NEXT:    lg %r14, 8(%r5)
+; CHECK-NEXT:    lg %r5, 0(%r5)
+; CHECK-NEXT:    lg %r13, 8(%r4)
+; CHECK-NEXT:    lg %r4, 0(%r4)
+; CHECK-NEXT:    stg %r14, 168(%r15)
+; CHECK-NEXT:    stg %r5, 160(%r15)
+; CHECK-NEXT:    stg %r13, 184(%r15)
+; CHECK-NEXT:    stg %r4, 176(%r15)
+; CHECK-NEXT:    stg %r3, 200(%r15)
+; CHECK-NEXT:    stg %r2, 192(%r15)
+; CHECK-NEXT:    stg %r1, 216(%r15)
+; CHECK-NEXT:    la %r2, 208(%r15)
+; CHECK-NEXT:    la %r3, 192(%r15)
+; CHECK-NEXT:    la %r4, 176(%r15)
+; CHECK-NEXT:    la %r5, 160(%r15)
+; CHECK-NEXT:    stg %r0, 208(%r15)
+; CHECK-NEXT:    brasl %r14, fnptr at PLT
+; CHECK-NEXT:    lmg %r13, %r15, 328(%r15)
+; CHECK-NEXT:    br %r14
+;
+; VECTOR-LABEL: fun1:
+; VECTOR:       # %bb.0:
+; VECTOR-NEXT:    stmg %r14, %r15, 112(%r15)
+; VECTOR-NEXT:    .cfi_offset %r14, -48
+; VECTOR-NEXT:    .cfi_offset %r15, -40
+; VECTOR-NEXT:    aghi %r15, -224
+; VECTOR-NEXT:    .cfi_def_cfa_offset 384
+; VECTOR-NEXT:    vl %v0, 0(%r2), 3
+; VECTOR-NEXT:    vl %v1, 0(%r3), 3
+; VECTOR-NEXT:    vl %v2, 0(%r4), 3
+; VECTOR-NEXT:    vl %v3, 0(%r5), 3
+; VECTOR-NEXT:    la %r2, 208(%r15)
+; VECTOR-NEXT:    la %r3, 192(%r15)
+; VECTOR-NEXT:    la %r4, 176(%r15)
+; VECTOR-NEXT:    la %r5, 160(%r15)
+; VECTOR-NEXT:    vst %v3, 160(%r15), 3
+; VECTOR-NEXT:    vst %v2, 176(%r15), 3
+; VECTOR-NEXT:    vst %v1, 192(%r15), 3
+; VECTOR-NEXT:    vst %v0, 208(%r15), 3
+; VECTOR-NEXT:    brasl %r14, fnptr at PLT
+; VECTOR-NEXT:    lmg %r14, %r15, 336(%r15)
+; VECTOR-NEXT:    br %r14
+  call void @fnptr(%Ty1 %A, %Ty1 %B)
+  ret void
+}
+
+%Ty2 = type {i256}
+define fastcc void @fun2(%Ty2 %A, %Ty2 %B) {
+; CHECK-LABEL: fun2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r13, %r15, 104(%r15)
+; CHECK-NEXT:    .cfi_offset %r13, -56
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -224
+; CHECK-NEXT:    .cfi_def_cfa_offset 384
+; CHECK-NEXT:    lg %r0, 0(%r2)
+; CHECK-NEXT:    lg %r1, 8(%r2)
+; CHECK-NEXT:    lg %r4, 16(%r2)
+; CHECK-NEXT:    lg %r2, 24(%r2)
+; CHECK-NEXT:    lg %r5, 24(%r3)
+; CHECK-NEXT:    lg %r14, 16(%r3)
+; CHECK-NEXT:    lg %r13, 8(%r3)
+; CHECK-NEXT:    lg %r3, 0(%r3)
+; CHECK-NEXT:    stg %r5, 184(%r15)
+; CHECK-NEXT:    stg %r14, 176(%r15)
+; CHECK-NEXT:    stg %r13, 168(%r15)
+; CHECK-NEXT:    stg %r3, 160(%r15)
+; CHECK-NEXT:    stg %r2, 216(%r15)
+; CHECK-NEXT:    stg %r4, 208(%r15)
+; CHECK-NEXT:    stg %r1, 200(%r15)
+; CHECK-NEXT:    la %r2, 192(%r15)
+; CHECK-NEXT:    la %r3, 160(%r15)
+; CHECK-NEXT:    stg %r0, 192(%r15)
+; CHECK-NEXT:    brasl %r14, fnptr at PLT
+; CHECK-NEXT:    lmg %r13, %r15, 328(%r15)
+; CHECK-NEXT:    br %r14
+;
+; VECTOR-LABEL: fun2:
+; VECTOR:       # %bb.0:
+; VECTOR-NEXT:    stmg %r14, %r15, 112(%r15)
+; VECTOR-NEXT:    .cfi_offset %r14, -48
+; VECTOR-NEXT:    .cfi_offset %r15, -40
+; VECTOR-NEXT:    aghi %r15, -224
+; VECTOR-NEXT:    .cfi_def_cfa_offset 384
+; VECTOR-NEXT:    vl %v0, 0(%r2), 3
+; VECTOR-NEXT:    vl %v1, 16(%r2), 3
+; VECTOR-NEXT:    vl %v2, 0(%r3), 3
+; VECTOR-NEXT:    vl %v3, 16(%r3), 3
+; VECTOR-NEXT:    la %r2, 192(%r15)
+; VECTOR-NEXT:    la %r3, 160(%r15)
+; VECTOR-NEXT:    vst %v3, 176(%r15), 3
+; VECTOR-NEXT:    vst %v2, 160(%r15), 3
+; VECTOR-NEXT:    vst %v1, 208(%r15), 3
+; VECTOR-NEXT:    vst %v0, 192(%r15), 3
+; VECTOR-NEXT:    brasl %r14, fnptr at PLT
+; VECTOR-NEXT:    lmg %r14, %r15, 336(%r15)
+; VECTOR-NEXT:    br %r14
+  call void @fnptr(%Ty2 %A, %Ty2 %B)
+  ret void
+}
+
+%Ty3 = type {float, i256, i32, i128, i8}
+define fastcc void @fun3(%Ty3 %A) {
+; CHECK-LABEL: fun3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r13, %r15, 104(%r15)
+; CHECK-NEXT:    .cfi_offset %r13, -56
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -208
+; CHECK-NEXT:    .cfi_def_cfa_offset 368
+; CHECK-NEXT:    lg %r0, 0(%r2)
+; CHECK-NEXT:    lg %r1, 8(%r2)
+; CHECK-NEXT:    lg %r14, 16(%r2)
+; CHECK-NEXT:    lg %r2, 24(%r2)
+; CHECK-NEXT:    lg %r13, 0(%r4)
+; CHECK-NEXT:    lg %r4, 8(%r4)
+; CHECK-NEXT:    stc %r5, 64
----------------
JonPsson1 wrote:

LowerArguments() calls ComputeValueTypes() for the incoming Arg which splits the struct into separate parts (types), which result in InputArg:s in Ins. SystemZ::LowerFormalArguments() then creates the ArgLocs based on Ins. Each ArgLoc is then treated as an argument as if it was passed separately to begin with, IIUC.  So

float in %f0, i256 indirectly in %2, i32 in %r3, i128 indirectly in %r4, i8 in %r5.

To see the registers used as arguments, the struct is first stored to memory (starting at address 0), otherwise they would just remain invisible when passed on to the call.

Then it is stored again as outgoing stack argument. The indirect elements (i256, i128) are passed via the same address registers, although stored in reverse order.



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


More information about the llvm-commits mailing list