[clang] [llvm] [SystemZ] Add i128 support for z/OS XPLINK64 ABI (PR #223026)
Zibi Sarbinowski via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 11 12:46:27 PDT 2026
https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/223026
>From f8b97cc936cc8e9ba56a32809d8abece43d2f074 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Fri, 11 Sep 2026 15:33:39 -0400
Subject: [PATCH 1/2] [SystemZ] Add i128 support for z/OS XPLINK64 ABI
Add support for passing and returning 128-bit integers (i128) in the
z/OS XPLINK64 calling convention using vector registers.
- Gate i128 availability on z/OS behind the vector facility (arch11/z13)
via hasInt128Type() override in the SystemZ target
- Add CC_XPLINK_Int128 custom handler that maps i128 to v16i8 for
register allocation in VR24-VR31
- Add CC_XPLINK_Int128 to RetCC_SystemZ_XPLINK64 and CC_SystemZ_XPLINK64
- Update convertLocVTToValVT/convertValVTToLocVT to handle i128<->v16i8
bitcast in addition to the existing i64->v2i64 short vector path
- Guard the i128>64-bit return stack spill path behind isTargetLinux()
so z/OS can return i128 in a vector register
- Add isTargetLinux() predicate to SystemZSubtarget
- Add driver check: -mvx with hard-float ABI requires at least arch11;
emit err_drv_incompatible_arch diagnostic for older -march values
- Pass Triple through to getSystemZTargetFeatures() so the -mvx arch
check has access to target information
- Add new diagnostic err_drv_incompatible_arch to DiagnosticDriverKinds.td
Tests:
- clang/test/CodeGen/SystemZ/zos-alignment.c: gate __int128 layout test
behind -target-cpu z13
- clang/test/CodeGen/zos-abi-int128-error.c: verify i128 is rejected
without vector facility and -mvx arch check
- clang/test/Sema/zvector.c: add z/OS zvector test variant
- llvm/test/CodeGen/SystemZ/zos-abi-int128-64.ll: end-to-end ABI test
for i128 argument/return passing in VRs
- llvm/test/CodeGen/SystemZ/call-zos-i128.ll: update to reflect new
vector register passing convention
- llvm/test/CodeGen/SystemZ/int-div-09.ll: i128 arg to v16i8 conversion
---
.../clang/Basic/DiagnosticDriverKinds.td | 3 +
clang/lib/Basic/Targets/SystemZ.h | 5 +
clang/lib/Driver/ToolChains/Arch/SystemZ.cpp | 33 ++-
clang/lib/Driver/ToolChains/Arch/SystemZ.h | 3 +-
clang/lib/Driver/ToolChains/CommonArgs.cpp | 2 +-
clang/test/CodeGen/SystemZ/zos-alignment.c | 10 +-
clang/test/CodeGen/zos-abi-int128-error.c | 16 ++
clang/test/Sema/zvector.c | 4 +
llvm/lib/Target/SystemZ/SystemZCallingConv.h | 8 +
llvm/lib/Target/SystemZ/SystemZCallingConv.td | 7 +
.../Target/SystemZ/SystemZISelLowering.cpp | 29 +-
llvm/lib/Target/SystemZ/SystemZSubtarget.h | 3 +
llvm/test/CodeGen/SystemZ/call-zos-i128.ll | 14 +-
llvm/test/CodeGen/SystemZ/int-div-09.ll | 13 +
.../test/CodeGen/SystemZ/zos-abi-int128-64.ll | 253 ++++++++++++++++++
15 files changed, 370 insertions(+), 33 deletions(-)
create mode 100644 clang/test/CodeGen/zos-abi-int128-error.c
create mode 100644 llvm/test/CodeGen/SystemZ/int-div-09.ll
create mode 100644 llvm/test/CodeGen/SystemZ/zos-abi-int128-64.ll
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index df6ff0c2cf399..7e872c5da674d 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -203,6 +203,9 @@ def err_drv_unsupported_unwind_for_platform : Error<
"unsupported unwind library '%0' for platform '%1'">;
def err_drv_incompatible_unwindlib : Error<
"--rtlib=libgcc requires --unwindlib=libgcc">;
+def err_drv_incompatible_arch : Error<
+ "'%0' option requires at least '%1', current '%2' is too low">;
+
def err_drv_incompatible_options : Error<
"the combination of '%0' and '%1' is incompatible">;
def err_drv_invalid_cstdlib_name : Error<
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index cf7d940c1b2e5..6aaf47fb44f5b 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -261,6 +261,11 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
uint64_t getPointerAlignV(LangAS AddrSpace) const override {
return getPointerWidthV(AddrSpace);
}
+
+ bool hasInt128Type() const override {
+ return (getTriple().isOSzOS() ? hasFeature("vx")
+ : TargetInfo::hasInt128Type());
+ }
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
index 1ef6a725483e8..583d04d8c84f5 100644
--- a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
@@ -53,7 +53,8 @@ std::string systemz::getSystemZTargetCPU(const ArgList &Args,
return CLANG_SYSTEMZ_DEFAULT_ARCH;
}
-void systemz::getSystemZTargetFeatures(const Driver &D, const ArgList &Args,
+void systemz::getSystemZTargetFeatures(const Driver &D, const llvm::Triple &T,
+ const ArgList &Args,
std::vector<llvm::StringRef> &Features) {
// -m(no-)htm overrides use of the transactional-execution facility.
if (Arg *A = Args.getLastArg(options::OPT_mhtm, options::OPT_mno_htm)) {
@@ -62,18 +63,36 @@ void systemz::getSystemZTargetFeatures(const Driver &D, const ArgList &Args,
else
Features.push_back("-transactional-execution");
}
+
+ bool HardFloatABI = false;
+ systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args);
+ if (FloatABI == systemz::FloatABI::Soft)
+ Features.push_back("+soft-float");
+ else if (FloatABI == systemz::FloatABI::Hard)
+ HardFloatABI = true;
+
// -m(no-)vx overrides use of the vector facility.
if (Arg *A = Args.getLastArg(options::OPT_mvx, options::OPT_mno_vx)) {
- if (A->getOption().matches(options::OPT_mvx))
+
+ // The -mvx requires at least -march=arch11/z13 when -mhard-float is in affect.
+ if (A->getOption().matches(options::OPT_mvx)) {
+ auto Arch = getSystemZTargetCPU(Args, T);
+ if (HardFloatABI && llvm::StringSwitch<bool>(Arch)
+ .Case("arch8", true)
+ .Case("z10", true)
+ .Case("arch9", true)
+ .Case("z196", true)
+ .Case("arch10", true)
+ .Case("zEC12", true)
+ .Default(false)) {
+ D.Diag(diag::err_drv_incompatible_arch)
+ << "-mvx" << "-march=arch11" << Arch;
+ }
Features.push_back("+vector");
- else
+ } else
Features.push_back("-vector");
}
- systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args);
- if (FloatABI == systemz::FloatABI::Soft)
- Features.push_back("+soft-float");
-
if (const Arg *A = Args.getLastArg(options::OPT_munaligned_symbols,
options::OPT_mno_unaligned_symbols)) {
if (A->getOption().matches(options::OPT_munaligned_symbols))
diff --git a/clang/lib/Driver/ToolChains/Arch/SystemZ.h b/clang/lib/Driver/ToolChains/Arch/SystemZ.h
index f2d30d24ba63c..b5d1da71f58e1 100644
--- a/clang/lib/Driver/ToolChains/Arch/SystemZ.h
+++ b/clang/lib/Driver/ToolChains/Arch/SystemZ.h
@@ -30,7 +30,8 @@ FloatABI getSystemZFloatABI(const Driver &D, const llvm::opt::ArgList &Args);
std::string getSystemZTargetCPU(const llvm::opt::ArgList &Args,
const llvm::Triple &T);
-void getSystemZTargetFeatures(const Driver &D, const llvm::opt::ArgList &Args,
+void getSystemZTargetFeatures(const Driver &D, const llvm::Triple &T,
+ const llvm::opt::ArgList &Args,
std::vector<llvm::StringRef> &Features);
} // end namespace systemz
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 64859a318485b..dba514adbff8f 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -922,7 +922,7 @@ void tools::getTargetFeatures(const Driver &D, const llvm::Triple &Triple,
riscv::getRISCVTargetFeatures(D, Triple, Args, Features);
break;
case llvm::Triple::systemz:
- systemz::getSystemZTargetFeatures(D, Args, Features);
+ systemz::getSystemZTargetFeatures(D, Triple, Args, Features);
break;
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_32:
diff --git a/clang/test/CodeGen/SystemZ/zos-alignment.c b/clang/test/CodeGen/SystemZ/zos-alignment.c
index 65c6843e0d9ac..a9fe32e248157 100644
--- a/clang/test/CodeGen/SystemZ/zos-alignment.c
+++ b/clang/test/CodeGen/SystemZ/zos-alignment.c
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -emit-llvm-only -triple s390x-none-zos -fdump-record-layouts %s | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -emit-llvm-only -triple s390x-none-zos -fdump-record-layouts -DINT128_A \
+// RUN: -target-cpu z13 %s | FileCheck %s --check-prefix=INT128
// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos %s -o - | FileCheck %s --check-prefix=DECL
static int __attribute__((aligned(32))) v0;
@@ -160,12 +162,14 @@ struct s11 {
// CHECK-NEXT: 8 | char b
// CHECK-NEXT: | [sizeof=16, align=8]
+#ifdef INT128_A
struct s12 {
__int128_t a;
} S12;
-// CHECK: 0 | struct s12
-// CHECK-NEXT: 0 | __int128_t a
-// CHECK-NEXT: | [sizeof=16, align=8]
+// INT128: 0 | struct s12
+// INT128-NEXT: 0 | __int128_t a
+// INT128-NEXT: | [sizeof=16, align=8]
+#endif
union u0 {
unsigned short d1 __attribute__((packed));
diff --git a/clang/test/CodeGen/zos-abi-int128-error.c b/clang/test/CodeGen/zos-abi-int128-error.c
new file mode 100644
index 0000000000000..2f5eff1a885c7
--- /dev/null
+++ b/clang/test/CodeGen/zos-abi-int128-error.c
@@ -0,0 +1,16 @@
+// The following verifies that __int128 is not supported
+// RUN: %clang_cc1 -triple s390x-unknown-zos -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple s390x-unknown-zos -fsyntax-only -target-cpu arch10 -verify %s
+// RUN: not %clang -msoft-float --target=s390x-unknown-zos %s 2> %t.err
+// RUN: FileCheck -check-prefix=CHECK-1 %s < %t.err
+// RUN: not %clang -mvx -march=zEC12 --target=s390x-unknown-linux-gnu %s 2> %t.err
+// RUN: FileCheck -check-prefix=CHECK-2 %s < %t.err
+// RUN: not %clang -mvx -march=zEC12 -mhard-float --target=s390x-unknown-linux-gnu %s 2> %t.err
+// RUN: FileCheck -check-prefix=CHECK-2 %s < %t.err
+// RUN: not %clang -mvx -march=zEC12 -msoft-float --target=s390x-unknown-linux-gnu %s 2> %t.err
+// RUN: not FileCheck -check-prefix=CHECK-2 %s < %t.err
+
+__int128 pass_int128(__int128 arg) { return arg; } // expected-error {{__int128 is not supported on this target}} expected-error {{__int128 is not supported on this target}}
+
+// CHECK-1: error: unsupported option '-msoft-float' for target 's390x-unknown-zos'
+// CHECK-2: error: '-mvx' option requires at least '-march=arch11', current 'zEC12' is too low
diff --git a/clang/test/Sema/zvector.c b/clang/test/Sema/zvector.c
index e1e4ab532426d..e3ceac48faf0b 100644
--- a/clang/test/Sema/zvector.c
+++ b/clang/test/Sema/zvector.c
@@ -2,6 +2,10 @@
// RUN: -flax-vector-conversions=none -W -Wall -Wconversion \
// RUN: -Werror -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple s390x-ibm-zos -fzvector -target-cpu z13 \
+// RUN: -flax-vector-conversions=none -W -Wall -Wconversion \
+// RUN: -Werror -fsyntax-only -verify %s
+
vector signed char sc, sc2;
vector unsigned char uc, uc2;
vector bool char bc, bc2;
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.h b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
index 9fcd5b8152c5a..88f532125abe3 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.h
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
@@ -96,6 +96,14 @@ inline bool CC_XPLINK64_Pointer(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
return false;
}
+inline bool CC_XPLINK_Int128(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
+ CCValAssign::LocInfo &LocInfo,
+ ISD::ArgFlagsTy &ArgFlags, CCState &State) {
+ LocVT = MVT::v16i8;
+ LocInfo = CCValAssign::BCvt;
+ return false;
+}
+
inline bool CC_XPLINK64_Shadow_Reg(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
index 4f483cf2479dd..7263587d54369 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
@@ -167,6 +167,9 @@ def CSR_SystemZ_XPLINK64_Vector : CalleeSavedRegs<(add CSR_SystemZ_XPLINK64,
// z/OS XPLINK64 return value calling convention
//===----------------------------------------------------------------------===//
def RetCC_SystemZ_XPLINK64 : CallingConv<[
+ // Convert an i128 value to vector
+ CCIfType<[i128], CCCustom<"CC_XPLINK_Int128">>,
+
// XPLINK64 ABI compliant code widens integral types smaller than i64
// to i64.
CCIfType<[i32], CCPromoteToType<i64>>,
@@ -225,6 +228,10 @@ def CC_SystemZ_XPLINK64 : CallingConv<[
CCIfPtr<CCCustom<"CC_XPLINK64_Pointer">>,
// long double, can only be passed in GPR2 and GPR3, if available,
// hence R2Q
+
+ // Convert an i128 value to vector
+ CCIfType<[i128], CCCustom<"CC_XPLINK_Int128">>,
+
CCIfType<[f128], CCIfArgVarArg<CCCustom<"CC_XPLINK64_Allocate128BitVararg">>>,
// Non fixed vector arguments are treated in the same way as long
// doubles.
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 8e621aafe27d2..f93cbbfb557ca 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -1900,11 +1900,15 @@ static SDValue convertLocVTToValVT(SelectionDAG &DAG, const SDLoc &DL,
if (VA.isExtInLoc())
Value = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Value);
else if (VA.getLocInfo() == CCValAssign::BCvt) {
- // If this is a short vector argument loaded from the stack,
- // extend from i64 to full vector size and then bitcast.
- assert(VA.getLocVT() == MVT::i64);
- assert(VA.getValVT().isVector());
- Value = DAG.getBuildVector(MVT::v2i64, DL, {Value, DAG.getUNDEF(MVT::i64)});
+ // If the argument is a short vector loaded from the stack,
+ // extend it from i64 to the full vector size and then perform a bitcast.
+ // Alternatively, if the argument is an int128,
+ // directly bitcast it into a vector of v16i8.
+ assert(VA.getLocVT() == MVT::i64 || VA.getLocVT() == MVT::v16i8);
+ assert(VA.getValVT().isVector() || VA.getValVT() == MVT::i128);
+ if (VA.getLocVT() == MVT::i64)
+ Value =
+ DAG.getBuildVector(MVT::v2i64, DL, {Value, DAG.getUNDEF(MVT::i64)});
Value = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Value);
} else
assert(VA.getLocInfo() == CCValAssign::Full && "Unsupported getLocInfo");
@@ -1924,9 +1928,11 @@ static SDValue convertValVTToLocVT(SelectionDAG &DAG, const SDLoc &DL,
case CCValAssign::AExt:
return DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Value);
case CCValAssign::BCvt: {
- assert(VA.getLocVT() == MVT::i64 || VA.getLocVT() == MVT::i128);
+ assert(VA.getLocVT() == MVT::i64 || VA.getLocVT() == MVT::i128 ||
+ VA.getLocVT() == MVT::v16i8);
assert(VA.getValVT().isVector() || VA.getValVT() == MVT::f32 ||
- VA.getValVT() == MVT::f64 || VA.getValVT() == MVT::f128);
+ VA.getValVT() == MVT::f64 || VA.getValVT() == MVT::f128 ||
+ VA.getValVT() == MVT::i128);
// For an f32 vararg we need to first promote it to an f64 and then
// bitcast it to an i64.
if (VA.getValVT() == MVT::f32 && VA.getLocVT() == MVT::i64)
@@ -2608,9 +2614,12 @@ bool SystemZTargetLowering::CanLowerReturn(
const Type *RetTy) const {
// Special case that we cannot easily detect in RetCC_SystemZ since
// i128 may not be a legal type.
- for (auto &Out : Outs)
- if (Out.ArgVT.isScalarInteger() && Out.ArgVT.getSizeInBits() > 64)
- return false;
+ // On z/OS we need to skip the convention of passing the return value on
+ // the stack used on zLinux.
+ if (Subtarget.isTargetLinux())
+ for (auto &Out : Outs)
+ if (Out.ArgVT.isScalarInteger() && Out.ArgVT.getSizeInBits() > 64)
+ return false;
SmallVector<CCValAssign, 16> RetLocs;
CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, Context);
diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.h b/llvm/lib/Target/SystemZ/SystemZSubtarget.h
index 761bc525b59d3..eda9f095fb237 100644
--- a/llvm/lib/Target/SystemZ/SystemZSubtarget.h
+++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.h
@@ -124,6 +124,9 @@ class SystemZSubtarget : public SystemZGenSubtargetInfo {
// Returns TRUE if we are generating code for a s390x machine running zOS
bool isTargetzOS() const { return TargetTriple.isOSzOS(); }
+
+ // Returns TRUE if we are generating code for a s390x machine running Linux
+ bool isTargetLinux() const { return TargetTriple.isOSLinux(); }
};
} // end namespace llvm
diff --git a/llvm/test/CodeGen/SystemZ/call-zos-i128.ll b/llvm/test/CodeGen/SystemZ/call-zos-i128.ll
index c12e26184f068..e5285377c939e 100644
--- a/llvm/test/CodeGen/SystemZ/call-zos-i128.ll
+++ b/llvm/test/CodeGen/SystemZ/call-zos-i128.ll
@@ -4,14 +4,9 @@
; CHECK-LABEL: call_i128 DS 0H
; CHECK-DAG: larl 1,L#CPI0_0
-; CHECK-DAG: vl 0,0(1),3
-; CHECK-DAG: vst 0,2256(4),3
+; CHECK-DAG: vl 24,0(1),3
; CHECK-DAG: larl 1,L#CPI0_1
-; CHECK-DAG: vl 0,0(1),3
-; CHECK-DAG: vst 0,2272(4),3
-; CHECK-DAG: la 1,2288(4)
-; CHECK-DAG: la 2,2272(4)
-; CHECK-DAG: la 3,2256(4)
+; CHECK-DAG: vl 25,0(1),3
define i128 @call_i128() {
entry:
@@ -20,10 +15,7 @@ entry:
}
; CHECK-LABEL: pass_i128 DS 0H
-; CHECK: vl 0,0(3),3
-; CHECK: vl 1,0(2),3
-; CHECK: vaq 0,1,0
-; CHECK: vst 0,0(1),3
+; CHECK: vaq 24,24,25
define i128 @pass_i128(i128 %arg0, i128 %arg1) {
entry:
%N = add i128 %arg0, %arg1
diff --git a/llvm/test/CodeGen/SystemZ/int-div-09.ll b/llvm/test/CodeGen/SystemZ/int-div-09.ll
new file mode 100644
index 0000000000000..bd77d62bf23ca
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/int-div-09.ll
@@ -0,0 +1,13 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; The objective of this test is to check conversion from i128 argument to vector v16i8.
+; This test was derived from builtins/floatuntitf.c residing in compiler.rt.
+
+; RUN: env LLVM_EMIT_GNU_AS_ZOS=0 llc -mtriple s390x-ibm-zos < %s | FileCheck %s
+$test_i128_arg = comdat any
+define void @test_i128_arg(ptr %this, i128 %__arg) "target-features"="+vector" {
+; CHECK-LABEL: test_i128_arg DS 0H
+; CHECK: * %entry
+; CHECK-NEXT: * %bb.0:
+entry:
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/zos-abi-int128-64.ll b/llvm/test/CodeGen/SystemZ/zos-abi-int128-64.ll
new file mode 100644
index 0000000000000..afbd5f0b0d5e9
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-abi-int128-64.ll
@@ -0,0 +1,253 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: env LLVM_EMIT_GNU_AS_ZOS=0 llc < %s| FileCheck %s
+
+source_filename = "zos-abi-int128-input.c"
+target datalayout = "E-m:l-p1:32:32-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64"
+target triple = "s390x-ibm-zos"
+
+attributes #0 = { "target-cpu"="z13" "target-features"="+vector" }
+
+; Check if GPR1 is used for the first integer argument and
+; VR24 for the second int128 argument and return value.
+;
+define i128 @pi_1i128(i32 signext %a1, i128 %a2) #0 {
+; CHECK-LABEL: pi_1i128 DS 0H
+; CHECK: stmg 6,7,1904(4)
+; CHECK-NEXT: L#stack_update0 DS 0H
+; CHECK-NEXT: aghi 4,-160
+; CHECK: L#end_of_prologue0 DS 0H
+; CHECK-NEXT: vlvgp 0,1,1
+; CHECK-NEXT: vrepf 0,0,3
+; CHECK-NEXT: vrepib 1,96
+; CHECK-NEXT: st 1,2204(4)
+; CHECK-NEXT: vst 24,2184(4),3
+; CHECK-NEXT: vsrab 0,0,1
+; CHECK-NEXT: lg 7,2072(4)
+; CHECK-NEXT: vaq 24,0,24
+; CHECK-NEXT: aghi 4,160
+; CHECK-NEXT: b 2(7)
+entry:
+ %a1.addr = alloca i32, align 4
+ %a2.addr = alloca i128, align 8
+ store i32 %a1, ptr %a1.addr, align 4
+ store i128 %a2, ptr %a2.addr, align 8
+ %0 = load i32, ptr %a1.addr, align 4
+ %conv = sext i32 %0 to i128
+ %1 = load i128, ptr %a2.addr, align 8
+ %add = add nsw i128 %conv, %1
+ ret i128 %add
+}
+
+; Check if VR24-31 are used for the first 8 int128 arguments and
+; the 9th int128 argument is loaded from the stack into VR0.
+; In addition, check VR24 is used for return value.
+;
+define i128 @p9i128(i128 %a1, i128 %a2, i128 %a3, i128 %a4, i128 %a5, i128 %a6, i128 %a7, i128 %a8, i128 %a9) #0 {
+; CHECK-LABEL: p9i128 DS 0H
+; CHECK: stmg 6,7,1776(4)
+; CHECK-NEXT: L#stack_update1 DS 0H
+; CHECK-NEXT: aghi 4,-288
+; CHECK: L#end_of_prologue1 DS 0H
+; CHECK-NEXT: vaq 1,24,25
+; CHECK-NEXT: vaq 1,1,26
+; CHECK-NEXT: vaq 1,1,27
+; CHECK-NEXT: vl 0,2592(4),4
+; CHECK-NEXT: vaq 1,1,28
+; CHECK-NEXT: vaq 1,1,29
+; CHECK-NEXT: vst 24,2320(4),3
+; CHECK-NEXT: vst 25,2304(4),3
+; CHECK-NEXT: vst 26,2288(4),3
+; CHECK-NEXT: vst 27,2272(4),3
+; CHECK-NEXT: vst 28,2256(4),3
+; CHECK-NEXT: vst 29,2240(4),3
+; CHECK-NEXT: vst 30,2224(4),3
+; CHECK-NEXT: vst 31,2208(4),3
+; CHECK-NEXT: vst 0,2192(4),3
+; CHECK-NEXT: vaq 1,1,30
+; CHECK-NEXT: lg 7,2072(4)
+; CHECK-NEXT: vaq 1,1,31
+; CHECK-NEXT: vaq 24,1,0
+; CHECK-NEXT: aghi 4,288
+; CHECK-NEXT: b 2(7)
+entry:
+ %a1.addr = alloca i128, align 8
+ %a2.addr = alloca i128, align 8
+ %a3.addr = alloca i128, align 8
+ %a4.addr = alloca i128, align 8
+ %a5.addr = alloca i128, align 8
+ %a6.addr = alloca i128, align 8
+ %a7.addr = alloca i128, align 8
+ %a8.addr = alloca i128, align 8
+ %a9.addr = alloca i128, align 8
+ store i128 %a1, ptr %a1.addr, align 8
+ store i128 %a2, ptr %a2.addr, align 8
+ store i128 %a3, ptr %a3.addr, align 8
+ store i128 %a4, ptr %a4.addr, align 8
+ store i128 %a5, ptr %a5.addr, align 8
+ store i128 %a6, ptr %a6.addr, align 8
+ store i128 %a7, ptr %a7.addr, align 8
+ store i128 %a8, ptr %a8.addr, align 8
+ store i128 %a9, ptr %a9.addr, align 8
+ %0 = load i128, ptr %a1.addr, align 8
+ %1 = load i128, ptr %a2.addr, align 8
+ %add = add nsw i128 %0, %1
+ %2 = load i128, ptr %a3.addr, align 8
+ %add1 = add nsw i128 %add, %2
+ %3 = load i128, ptr %a4.addr, align 8
+ %add2 = add nsw i128 %add1, %3
+ %4 = load i128, ptr %a5.addr, align 8
+ %add3 = add nsw i128 %add2, %4
+ %5 = load i128, ptr %a6.addr, align 8
+ %add4 = add nsw i128 %add3, %5
+ %6 = load i128, ptr %a7.addr, align 8
+ %add5 = add nsw i128 %add4, %6
+ %7 = load i128, ptr %a8.addr, align 8
+ %add6 = add nsw i128 %add5, %7
+ %8 = load i128, ptr %a9.addr, align 8
+ %add7 = add nsw i128 %add6, %8
+ ret i128 %add7
+}
+
+; Check if three int128 arguments in addition to variable argument(s) are passed in extended argument list.
+;
+define i128 @p3i128_var(i128 %a1, i128 %a2, i128 %a3, ...) #0 {
+; CHECK-LABEL: p3i128_var DS 0H
+; CHECK: stmg 6,7,1840(4)
+; CHECK-NEXT: L#stack_update2 DS 0H
+; CHECK-NEXT: aghi 4,-224
+; CHECK-NEXT: stg 1,2400(4)
+; CHECK-NEXT: stg 2,2408(4)
+; CHECK-NEXT: stg 3,2416(4)
+; CHECK: L#end_of_prologue2 DS 0H
+; CHECK-NEXT: vl 0,2448(4),4
+; CHECK-NEXT: la 0,2464(4)
+; CHECK-NEXT: vst 24,2256(4),3
+; CHECK-NEXT: vst 25,2240(4),3
+; CHECK-NEXT: vst 26,2224(4),3
+; CHECK-NEXT: stg 0,2200(4)
+; CHECK-NEXT: vst 0,2208(4),3
+; CHECK-NEXT: vaq 1,24,25
+; CHECK-NEXT: lg 7,2072(4)
+; CHECK-NEXT: vaq 1,1,26
+; CHECK-NEXT: vsq 24,1,0
+; CHECK-NEXT: aghi 4,224
+; CHECK-NEXT: b 2(7)
+entry:
+ %a1.addr = alloca i128, align 8
+ %a2.addr = alloca i128, align 8
+ %a3.addr = alloca i128, align 8
+ %a4 = alloca i128, align 8
+ %args = alloca ptr, align 8
+ store i128 %a1, ptr %a1.addr, align 8
+ store i128 %a2, ptr %a2.addr, align 8
+ store i128 %a3, ptr %a3.addr, align 8
+ call void @llvm.va_start.p0(ptr %args)
+ %argp.cur = load ptr, ptr %args, align 8
+ %argp.next = getelementptr inbounds i8, ptr %argp.cur, i64 16
+ store ptr %argp.next, ptr %args, align 8
+ %0 = load i128, ptr %argp.cur, align 8
+ store i128 %0, ptr %a4, align 8
+ call void @llvm.va_end.p0(ptr %args)
+ %1 = load i128, ptr %a1.addr, align 8
+ %2 = load i128, ptr %a2.addr, align 8
+ %add = add nsw i128 %1, %2
+ %3 = load i128, ptr %a3.addr, align 8
+ %add1 = add nsw i128 %add, %3
+ %4 = load i128, ptr %a4, align 8
+ %sub = sub nsw i128 %add1, %4
+ ret i128 %sub
+}
+
+; Check the call side for above functions.
+;
+define signext i32 @main() #0 {
+; CHECK-LABEL: main DS 0H
+; CHECK: stmg 6,8,1584(4)
+; CHECK-NEXT: L#stack_update3 DS 0H
+; CHECK-NEXT: aghi 4,-480
+; CHECK: L#end_of_prologue3 DS 0H
+; CHECK-NEXT: vgbm 0,0
+; CHECK-NEXT: vgbm 24,0
+; CHECK-NEXT: lgr 8,5
+; CHECK-NEXT: lg 6,8(5)
+; CHECK-NEXT: lg 5,0(5)
+; CHECK-NEXT: lghi 1,10
+; CHECK-NEXT: mvhi 2524(4),0
+; CHECK-NEXT: vst 0,2504(4),3
+; CHECK-NEXT: basr 7,6
+; CHECK-NEXT: bcr 0,0
+; CHECK-NEXT: vl 0,2504(4),3
+; CHECK-NEXT: vaq 24,0,24
+; CHECK-NEXT: vl 0,2376(4),3
+; CHECK-NEXT: vl 31,2392(4),3
+; CHECK-NEXT: vl 30,2408(4),3
+; CHECK-NEXT: vl 29,2424(4),3
+; CHECK-NEXT: vl 28,2440(4),3
+; CHECK-NEXT: vl 27,2456(4),3
+; CHECK-NEXT: vl 26,2472(4),3
+; CHECK-NEXT: vl 25,2488(4),3
+; CHECK-NEXT: lg 6,24(8)
+; CHECK-NEXT: lg 5,16(8)
+; CHECK-NEXT: vst 24,2504(4),3
+; CHECK-NEXT: vst 0,2304(4),3
+; CHECK-NEXT: basr 7,6
+; CHECK-NEXT: bcr 0,0
+; CHECK-NEXT: vl 0,2504(4),3
+; CHECK-NEXT: vaq 24,0,24
+; CHECK-NEXT: vl 0,2456(4),3
+; CHECK-NEXT: vl 26,2472(4),3
+; CHECK-NEXT: vl 25,2488(4),3
+; CHECK-NEXT: lg 6,40(8)
+; CHECK-NEXT: lg 5,32(8)
+; CHECK-NEXT: vst 24,2504(4),3
+; CHECK-NEXT: vst 0,2224(4),3
+; CHECK-NEXT: basr 7,6
+; CHECK-NEXT: bcr 0,0
+; CHECK-NEXT: vl 0,2504(4),3
+; CHECK-NEXT: vaq 0,0,24
+; CHECK-NEXT: vst 0,2504(4),3
+; CHECK-NEXT: lmg 7,8,2072(4)
+; CHECK-NEXT: lghi 3,0
+; CHECK-NEXT: aghi 4,480
+; CHECK-NEXT: b 2(7)
+entry:
+ %retval = alloca i32, align 4
+ %a1 = alloca i128, align 8
+ %a2 = alloca i128, align 8
+ %a3 = alloca i128, align 8
+ %s4 = alloca i128, align 8
+ %a5 = alloca i128, align 8
+ %a6 = alloca i128, align 8
+ %a7 = alloca i128, align 8
+ %a8 = alloca i128, align 8
+ %a9 = alloca i128, align 8
+ store i32 0, ptr %retval, align 4
+ store i128 0, ptr %a1, align 8
+ %0 = load i128, ptr %a1, align 8
+ %call = call i128 @pi_1i128(i32 signext 10, i128 %0)
+ %1 = load i128, ptr %a1, align 8
+ %add = add nsw i128 %1, %call
+ store i128 %add, ptr %a1, align 8
+ %2 = load i128, ptr %a1, align 8
+ %3 = load i128, ptr %a2, align 8
+ %4 = load i128, ptr %a3, align 8
+ %5 = load i128, ptr %s4, align 8
+ %6 = load i128, ptr %a5, align 8
+ %7 = load i128, ptr %a6, align 8
+ %8 = load i128, ptr %a7, align 8
+ %9 = load i128, ptr %a8, align 8
+ %10 = load i128, ptr %a9, align 8
+ %call1 = call i128 @p9i128(i128 %2, i128 %3, i128 %4, i128 %5, i128 %6, i128 %7, i128 %8, i128 %9, i128 %10)
+ %11 = load i128, ptr %a1, align 8
+ %add2 = add nsw i128 %11, %call1
+ store i128 %add2, ptr %a1, align 8
+ %12 = load i128, ptr %a1, align 8
+ %13 = load i128, ptr %a2, align 8
+ %14 = load i128, ptr %a3, align 8
+ %15 = load i128, ptr %s4, align 8
+ %call3 = call i128 (i128, i128, i128, ...) @p3i128_var(i128 %12, i128 %13, i128 %14, i128 %15)
+ %16 = load i128, ptr %a1, align 8
+ %add4 = add nsw i128 %16, %call3
+ store i128 %add4, ptr %a1, align 8
+ ret i32 0
+}
>From 431747b3cd53d77b6d32974615931ea1f8965efb Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Fri, 11 Sep 2026 15:46:15 -0400
Subject: [PATCH 2/2] [SystemZ] clang-format fixes for i128 XPLINK64 ABI
changes
---
clang/lib/Driver/ToolChains/Arch/SystemZ.cpp | 17 +++++++++--------
llvm/lib/Target/SystemZ/SystemZCallingConv.h | 4 ++--
2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
index 583d04d8c84f5..69a06f1dddf46 100644
--- a/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
@@ -74,17 +74,18 @@ void systemz::getSystemZTargetFeatures(const Driver &D, const llvm::Triple &T,
// -m(no-)vx overrides use of the vector facility.
if (Arg *A = Args.getLastArg(options::OPT_mvx, options::OPT_mno_vx)) {
- // The -mvx requires at least -march=arch11/z13 when -mhard-float is in affect.
+ // The -mvx requires at least -march=arch11/z13 when -mhard-float is in
+ // affect.
if (A->getOption().matches(options::OPT_mvx)) {
auto Arch = getSystemZTargetCPU(Args, T);
if (HardFloatABI && llvm::StringSwitch<bool>(Arch)
- .Case("arch8", true)
- .Case("z10", true)
- .Case("arch9", true)
- .Case("z196", true)
- .Case("arch10", true)
- .Case("zEC12", true)
- .Default(false)) {
+ .Case("arch8", true)
+ .Case("z10", true)
+ .Case("arch9", true)
+ .Case("z196", true)
+ .Case("arch10", true)
+ .Case("zEC12", true)
+ .Default(false)) {
D.Diag(diag::err_drv_incompatible_arch)
<< "-mvx" << "-march=arch11" << Arch;
}
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.h b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
index 88f532125abe3..91a1fe8f12d96 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.h
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
@@ -97,8 +97,8 @@ inline bool CC_XPLINK64_Pointer(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
}
inline bool CC_XPLINK_Int128(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
- CCValAssign::LocInfo &LocInfo,
- ISD::ArgFlagsTy &ArgFlags, CCState &State) {
+ CCValAssign::LocInfo &LocInfo,
+ ISD::ArgFlagsTy &ArgFlags, CCState &State) {
LocVT = MVT::v16i8;
LocInfo = CCValAssign::BCvt;
return false;
More information about the cfe-commits
mailing list