[clang] [llvm] [SystemZ] Add i128 support for z/OS XPLINK64 ABI (PR #223026)
Zibi Sarbinowski via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 16 14:09:21 PDT 2026
zibi2 wrote:
Thanks for the review, Uli.
**On the `CC_XPLINK_Int128` / `CCPassIndirect` removal:**
You are correct — the `CC_XPLINK_Int128` mutation to `v16i8` and the `CCPassIndirect` path were unnecessary. We have simplified this in the upstream patch to do exactly what you suggested: add `i128` directly to the `CCIfType` lists alongside vectors. The updated patch removes `CC_XPLINK_Int128`, `CCPassIndirect<i64>`, and `CC_SystemZ_I128Indirect` entirely.
One implementation detail worth noting: the `lowerI128ToGR128` call in `LowerCall_XPLINK` needed a guard to distinguish between i128 assigned to a GR128 pair (e.g. R2Q for vararg) versus i128 assigned to a VR128 vector register (V24–V31). Without the guard, the decomposition into hi/lo GPR halves fired incorrectly for the vector register case, generating a spurious `vlgvg`/`vlvgp` round-trip. The fix is:
```cpp
if (VA.getLocVT() == MVT::i128 &&
!SystemZ::VR128BitRegClass.contains(VA.getLocReg()))
ArgValue = lowerI128ToGR128(DAG, ArgValue);
```
This is needed to correctly handle the i128 vararg case where `CC_XPLINK64_Allocate128BitVararg` assigns to R2Q.
**On gating `i128` behind the vector facility:**
On z/OS, unlike Linux ELF, there is no vector emulation for pre-z13 targets — vector types are not legal types when hasVector() is false. Since i128 uses VR128BitRegClass (same as vectors), it similarly requires the vector facility.
**On the `err_drv_incompatible_arch` diagnostic:**
You're right. The diagnostic was based on a false premise: z/OS has no vector emulation, but that doesn't matter when -mvx is present — +vector is applied on top of the CPU-implied bits, so -march=arch10 -mvx produces a valid arch10 + vector facility subtarget regardless. I'll remove the diagnostic in a follow-up commit.
**One ABI note for awareness:**
You're right that this is a concern since i128 shipped in the 2.2 compiler. Here's the concrete impact.
The simplification changes how GPRs are shadowed for i128 fixed arguments: the old CC_XPLINK_Int128 did not shadow any GPRs, while the new code adds i128 to CC_XPLINK_Shadow_Reg (consistent with 128-bit vectors, which shadow 2 GPRs). This shifts the GPR slot seen by subsequent vararg arguments.
Concrete example — callee(i128 %a, double %d, ...) on z10:
Before — i128 via CC_XPLINK_Int128, no GPR shadow, double vararg lands in GPR2:
lg 0, 8(1)
lg 3, 0(1)
stg 0, 2264(4) ; store i128 high to stack temp
stg 3, 2256(4) ; store i128 low to stack temp
la 1, 2256(4) ; GPR1 = pointer to i128
lgdr 2, f0 ; double vararg -> GPR2
After — i128 in V24, shadows GPR1+GPR2, double vararg lands in GPR3:
lgdr 3, f0 ; double vararg -> GPR3
The callee sees double in GPR2 (old) vs GPR3 (new). Any caller compiled with the 2.2 compiler that passes i128 as a fixed argument followed by f64 or vector varargs will be mismatched against a callee compiled with the new code.
https://github.com/llvm/llvm-project/pull/223026
More information about the llvm-commits
mailing list