https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/206833
>From 3939ab730a89a6f47fac90499e00773b69e787e4 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Tue, 30 Jun 2026 16:25:39 -0400 Subject: [PATCH 1/7] [SystemZ] Fix signext/zeroext handling in XPLINK64 calling convention For z/OS XPLINK64, the ABI specification mandates that scalar return values are sign- or zero-extended to 64 bits, but makes no such guarantee for arguments. Other compilers (e.g. xlc) do not extend argument values, so clang was incorrectly emitting a sign-extension instruction (lgfr) on the callee side for every incoming promotable integer argument, causing interoperability failures. Fix: - In ZOSXPLinkABIInfo::classifyArgumentType(), use getDirect() for sub-64-bit promotable integer types. The XPLINK64 ABI does not mandate any widening of integer arguments (per the z/OS Language Environment Vendor Interfaces spec); arguments are passed at their natural width with no sign- or zero-extension guarantee. This omits signext/zeroext from LLVM IR parameters, so the backend emits no extension instruction for incoming arguments (lgr plain copy). 64-bit types (long, unsigned long) fall through to getDirect() unchanged via the size guard. - classifyReturnType() is unchanged: return values still use getExtend() because the spec does mandate 64-bit extension there. - TargetLibraryInfo::initExtensionsForTriple() updated: z/OS is split from ELF SystemZ so that middle-end optimization passes synthesizing library calls set ShouldExtI32Param=false and ShouldExtI32Return=true, matching the XPLINK64 ABI. Tests updated: - clang/test/CodeGen/SystemZ/zos-abi.c: integer parameters no longer carry signext/zeroext or noext; return types still carry signext/zeroext. - clang/test/CodeGen/pragma-export.cpp: i32 parameters updated to match. --- clang/lib/CodeGen/Targets/SystemZ.cpp | 14 ++++++---- clang/test/CodeGen/SystemZ/zos-abi.c | 26 +++++++++---------- clang/test/CodeGen/pragma-export.cpp | 8 +++--- .../include/llvm/Analysis/TargetLibraryInfo.h | 11 +++++--- llvm/lib/Target/SystemZ/SystemZCallingConv.td | 10 ++++--- llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll | 2 +- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index a27fee633ead2..d2be96131c4db 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -618,13 +618,11 @@ bool ZOSXPLinkABIInfo::isPromotableIntegerTypeForABI(QualType Ty) const { return true; // In addition to the usual promotable integer types, we also need to - // extend all 32-bit types, since the ABI requires promotion to 64 bits. + // extend 32-bit types, since the ABI requires promotion to 64 bits. if (const BuiltinType *BT = Ty->getAs<BuiltinType>()) switch (BT->getKind()) { case BuiltinType::Int: case BuiltinType::UInt: - case BuiltinType::ULong: - case BuiltinType::Long: return true; default: break; @@ -825,9 +823,15 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), RAA == CGCXXABI::RAA_DirectInMemory); - // Integers and enums are extended to full register width. + // The XPLINK64 ABI does not mandate any widening of integer arguments; + // arguments are passed at their natural width with no sign- or zero-extension + // guarantee. Only return values are required to be widened (per the z/OS + // Language Environment Vendor Interfaces spec). Other compilers (e.g. xlc) + // leave the upper bits of an argument register unspecified, so emitting + // signext/zeroext on parameters would produce incorrect code when + // interoperating with xlc. if (isPromotableIntegerTypeForABI(Ty)) - return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty)); + return ABIArgInfo::getDirect(CGT.ConvertType(Ty)); // For non-C calling conventions, compound types passed by address copy. if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty)) diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c index 0a31b47d8dc58..b18a2c6a05857 100644 --- a/clang/test/CodeGen/SystemZ/zos-abi.c +++ b/clang/test/CodeGen/SystemZ/zos-abi.c @@ -22,22 +22,22 @@ // Scalar types char pass_char(char arg) { return arg; } -// CHECK-LABEL: define signext i8 @pass_char(i8 signext %{{.*}}) +// CHECK-LABEL: define signext i8 @pass_char(i8 %{{.*}}) signed char pass_schar(signed char arg) { return arg; } -// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}}) +// CHECK-LABEL: define signext i8 @pass_schar(i8 %{{.*}}) unsigned char pass_uchar(unsigned char arg) { return arg; } -// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}}) +// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 %{{.*}}) short pass_short(short arg) { return arg; } -// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}}) +// CHECK-LABEL: define signext i16 @pass_short(i16 %{{.*}}) int pass_int(int arg) { return arg; } -// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}}) +// CHECK-LABEL: define signext i32 @pass_int(i32 %{{.*}}) long pass_long(long arg) { return arg; } -// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}}) +// CHECK-LABEL: define i64 @pass_long(i64 %{{.*}}) long long pass_longlong(long long arg) { return arg; } // CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}}) @@ -53,7 +53,7 @@ long double pass_longdouble(long double arg) { return arg; } enum Color { Red, Blue }; enum Color pass_enum(enum Color arg) { return arg; } -// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}}) +// CHECK-LABEL: define zeroext i32 @pass_enum(i32 %{{.*}}) #ifdef TEST_VEC vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; @@ -472,19 +472,19 @@ struct Bad4 pass_Bad4(struct Bad4 arg) { return arg; } // ================================================================== union tu_char { char a; } __attribute__((transparent_union)); union tu_char pass_tu_char(union tu_char arg) { return arg; } -// CHECK-LABEL: define{{.*}} i8 @pass_tu_char(i8 signext %{{.*}}) +// CHECK-LABEL: define{{.*}} i8 @pass_tu_char(i8 %{{.*}}) union tu_short { short a; } __attribute__((transparent_union)); union tu_short pass_tu_short(union tu_short arg) { return arg; } -// CHECK-LABEL: define{{.*}} i16 @pass_tu_short(i16 signext %{{.*}}) +// CHECK-LABEL: define{{.*}} i16 @pass_tu_short(i16 %{{.*}}) union tu_int { int a; } __attribute__((transparent_union)); union tu_int pass_tu_int(union tu_int arg) { return arg; } -// CHECK-LABEL: define{{.*}} i32 @pass_tu_int(i32 signext %{{.*}}) +// CHECK-LABEL: define{{.*}} i32 @pass_tu_int(i32 %{{.*}}) union tu_long { long a; } __attribute__((transparent_union)); union tu_long pass_tu_long(union tu_long arg) { return arg; } -// CHECK-LABEL: define{{.*}} i64 @pass_tu_long(i64 signext %{{.*}}) +// CHECK-LABEL: define{{.*}} i64 @pass_tu_long(i64 %{{.*}}) union tu_ptr { void *a; } __attribute__((transparent_union)); union tu_ptr pass_tu_ptr(union tu_ptr arg) { return arg; } @@ -596,7 +596,7 @@ int va_int_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, int); } // CHECK: ret i32 [[VAL]] long va_long_e(__builtin_va_list l) { return __builtin_va_arg(l, long); } -// CHECK-LABEL: define signext i64 @va_long_e(ptr %{{.*}}) +// CHECK-LABEL: define i64 @va_long_e(ptr %{{.*}}) // CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8 // CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8 // CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8 @@ -606,7 +606,7 @@ long va_long_e(__builtin_va_list l) { return __builtin_va_arg(l, long); } // CHECK: ret i64 [[VAL]] long va_long_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, long); } -// CHECK-LABEL: define signext i64 @va_long_s(ptr %{{.*}}) +// CHECK-LABEL: define i64 @va_long_s(ptr %{{.*}}) // CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8 // CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8 // CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8 diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp index 531afbd659234..f70e64ea7a4ca 100644 --- a/clang/test/CodeGen/pragma-export.cpp +++ b/clang/test/CodeGen/pragma-export.cpp @@ -53,10 +53,10 @@ void f10(int) {} // CHECK: define hidden void @f0() // CHECK: define void @f1() // CHECK: define hidden void @_Z2f2dd(double noundef %0, double noundef %1) -// CHECK: define void @f2(i32 noundef signext %0) -// CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef signext %1) +// CHECK: define void @f2(i32 noundef %0) +// CHECK: define hidden void @_Z2f2ii(i32 noundef %0, i32 noundef %1) // CHECK: define hidden void @f3(double noundef %0) -// CHECK: define hidden void @_Z2f3id(i32 noundef signext %0, double noundef %1) +// CHECK: define hidden void @_Z2f3id(i32 noundef %0, double noundef %1) // CHECK: define hidden void @_Z2f3dd(double noundef %0, double noundef %1) // CHECK: define hidden void @f2b() // CHECK: define hidden void @_Z2t0v() @@ -65,5 +65,5 @@ void f10(int) {} // CHECK: define hidden void @_ZN2N02f5Ev() // CHECK: define hidden void @_ZN2N03f5aEv() // CHECK: define void @f10(double noundef %0) -// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0) +// CHECK: define hidden void @_Z3f10i(i32 noundef %0) diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h index 629b126db17c2..b97a7cf1a0e7c 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -453,13 +453,18 @@ class TargetLibraryInfo { ShouldExtI32Param = ShouldExtI32Return = false; ShouldSignExtI32Param = ShouldSignExtI32Return = false; - // PowerPC64, Sparc64, SystemZ need signext/zeroext on i32 parameters and - // returns corresponding to C-level ints and unsigned ints. + // PowerPC64, Sparc64, and SystemZ ELF need signext/zeroext on i32 + // parameters and returns corresponding to C-level ints and unsigned ints. if (T.isPPC64() || T.getArch() == Triple::sparcv9 || - T.getArch() == Triple::systemz) { + (T.getArch() == Triple::systemz && !T.isOSzOS())) { ShouldExtI32Param = true; ShouldExtI32Return = true; } + // z/OS XPLINK64 only extends return values; parameters are not extended + // per the XPLINK ABI spec (other compilers do not extend arguments). + if (T.getArch() == Triple::systemz && T.isOSzOS()) { + ShouldExtI32Return = true; + } // LoongArch, Mips, and riscv64, on the other hand, need signext on i32 // parameters corresponding to both signed and unsigned ints. if (T.isLoongArch() || T.isMIPS() || T.isRISCV64()) { diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td index 69202e3fcbc57..9ea09d12ea34a 100644 --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -213,9 +213,13 @@ def RetCC_SystemZ_XPLINK64 : CallingConv<[ // examples. def CC_SystemZ_XPLINK64 : CallingConv<[ - // XPLINK64 ABI compliant code widens integral types smaller than i64 - // to i64 before placing the parameters either on the stack or in registers. - CCIfType<[i32], CCIfExtend<CCPromoteToType<i64>>>, + // Callers must pass i32 arguments in full 64-bit registers. The XPLINK64 + // ABI does not mandate sign- or zero-extension in the upper 32 bits (that + // requirement applies only to return values), but the caller is still + // responsible for placing the value in a 64-bit register before the call. + // Other compilers (e.g. xlc) behave this way unconditionally, so we promote + // i32 to i64 here regardless of any signext/zeroext attribute. + CCIfType<[i32], CCPromoteToType<i64>>, // Promote f32 to f64 and bitcast to i64, if it needs to be passed in GPRs. // Although we assign the f32 vararg to be bitcast, it will first be promoted // to an f64 within convertValVTToLocVT(). diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll index 610c851798614..acbd624fd10ff 100644 --- a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll +++ b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll @@ -19,7 +19,7 @@ define void @fLargeOutArgArea() { ; CHECK-LABEL: L#EPM_fLargeOutArgArea_0 DS 0H ; CHECK: * Bit 1: 0 = Non-leaf function ; CHECK: * Bit 2: 0 = Does not use alloca -; CHECK: DC XL4'00000220' +; CHECK: DC XL4'00000260' ; CHECK: fLargeOutArgArea DS 0H %1 = load [33 x i32], ptr @GlobLargeS, align 4 call void @fLargeParm([33 x i32] inreg %1) >From 0b18fe4d40f89de6f099d175c6d76c5e1d8d8dc0 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Wed, 22 Jul 2026 11:17:35 -0400 Subject: [PATCH 2/7] [SystemZ][z/OS] Fix crashes in null and HLASM streamers Two crash fixes exposed when WYVERN_DEFAULT_TARGET_TRIPLE=s390x-ibm-zos is set globally (forcing all compilations to target z/OS): 1. createNullTargetStreamer (SystemZMCTargetDesc.cpp) The null streamer path is taken for -emit-codegen-only (e.g. when clang diagnoses __attribute__((error("..."))) at the backend). Commit c4b3d1d51da2 added two PPA1 fields that call getTargetStreamer()->createWordDiffExpr(), but the base class SystemZTargetStreamer::createWordDiffExpr() returns nullptr. Passing nullptr to emitValue() causes a SIGSEGV. Fix: mirror what createObjectTargetStreamer already does -- return a SystemZTargetGOFFStreamer for z/OS targets so that createWordDiffExpr() produces a real MCExpr instead of null. Fixes: clang/test/Frontend/backend-attribute-error-warning-optimize.c 2. SystemZHLASMAsmStreamer::finishImpl() (SystemZHLASMAsmStreamer.cpp) When iterating undefined registered symbols to emit EXTRN/WXTRN directives, the code assumed that any symbol with CodeData == ESD_EXE_DATA has a non-null ADA. However, common/BSS globals that go through AsmPrinter::emitGlobalVariable() receive MCSA_ELF_TypeObject (setting CodeData = ESD_EXE_DATA) but then take the emitCommonSymbol() path which bypasses SelectSectionForGlobal, so their ADA is never set. The unconditional getADA()->getParent() dereference crashes. Fix: guard the ESD_EXE_DATA branch with && Sym.getADA() so that symbols with no ADA fall through to the plain EXTRN output. Fixes: llvm/test/CodeGen/Generic/2014-02-05-OpaqueConstants.ll --- .../lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp | 2 +- llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp index 5bf04747af94a..998c0da31fb91 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp @@ -469,7 +469,7 @@ void SystemZHLASMAsmStreamer::finishImpl() { if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined()) continue; auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol)); - if (Sym.getCodeData() == GOFF::ESD_EXE_DATA) { + if (Sym.getCodeData() == GOFF::ESD_EXE_DATA && Sym.getADA()) { OS << Sym.getADA()->getParent()->getExternalName() << " CATTR PART(" << Sym.getName() << ")"; EmitEOL(); diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp index a0de6949f343e..205a780c8fbe6 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp @@ -227,6 +227,8 @@ createObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { static MCTargetStreamer * createNullTargetStreamer(MCStreamer &S) { + if (S.getContext().getTargetTriple().isOSzOS()) + return new SystemZTargetGOFFStreamer(S); return new SystemZTargetStreamer(S); } >From e6ecf88e5298fc936c8be6d86f226139f10234bb Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 27 Jul 2026 14:04:34 -0400 Subject: [PATCH 3/7] Revert "[SystemZ][z/OS] Fix crashes in null and HLASM streamers" This reverts commit 0b18fe4d40f89de6f099d175c6d76c5e1d8d8dc0. --- .../MCTargetDesc/SystemZHLASMAsmStreamer.cpp | 2 +- .../MCTargetDesc/SystemZMCTargetDesc.cpp | 2 -- llvm/lib/Target/SystemZ/SystemZCallingConv.td | 12 ++++------ llvm/test/CodeGen/SystemZ/call-zos-01.ll | 22 +++++++++---------- llvm/test/CodeGen/SystemZ/call-zos-vararg.ll | 4 ++-- llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll | 2 +- .../CodeGen/SystemZ/zos-ada-relocations.ll | 2 +- llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll | 2 +- 8 files changed, 21 insertions(+), 27 deletions(-) diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp index 998c0da31fb91..5bf04747af94a 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp @@ -469,7 +469,7 @@ void SystemZHLASMAsmStreamer::finishImpl() { if (Symbol.isTemporary() || !Symbol.isRegistered() || Symbol.isDefined()) continue; auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol)); - if (Sym.getCodeData() == GOFF::ESD_EXE_DATA && Sym.getADA()) { + if (Sym.getCodeData() == GOFF::ESD_EXE_DATA) { OS << Sym.getADA()->getParent()->getExternalName() << " CATTR PART(" << Sym.getName() << ")"; EmitEOL(); diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp index 205a780c8fbe6..a0de6949f343e 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.cpp @@ -227,8 +227,6 @@ createObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { static MCTargetStreamer * createNullTargetStreamer(MCStreamer &S) { - if (S.getContext().getTargetTriple().isOSzOS()) - return new SystemZTargetGOFFStreamer(S); return new SystemZTargetStreamer(S); } diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td index 9ea09d12ea34a..c886040bf38d1 100644 --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -213,13 +213,6 @@ def RetCC_SystemZ_XPLINK64 : CallingConv<[ // examples. def CC_SystemZ_XPLINK64 : CallingConv<[ - // Callers must pass i32 arguments in full 64-bit registers. The XPLINK64 - // ABI does not mandate sign- or zero-extension in the upper 32 bits (that - // requirement applies only to return values), but the caller is still - // responsible for placing the value in a 64-bit register before the call. - // Other compilers (e.g. xlc) behave this way unconditionally, so we promote - // i32 to i64 here regardless of any signext/zeroext attribute. - CCIfType<[i32], CCPromoteToType<i64>>, // Promote f32 to f64 and bitcast to i64, if it needs to be passed in GPRs. // Although we assign the f32 vararg to be bitcast, it will first be promoted // to an f64 within convertValVTToLocVT(). @@ -246,9 +239,12 @@ def CC_SystemZ_XPLINK64 : CallingConv<[ // If i128 is not legal, such values are already split into two i64 here, // so we have to use a custom handler. CCIfType<[i64], CCCustom<"CC_SystemZ_I128Indirect">>, - // The first 3 integer arguments are passed in registers R1D-R3D. + // The first 3 integer arguments are passed in registers R1-R3. + // i32 uses the low-word sub-registers but occupies a full 8-byte slot, + // matching the XPLINK64 requirement that each parameter area word is 8 bytes. // The rest will be passed in the user area. The address offset of the user // area can be found in register R4D. + CCIfType<[i32], CCAssignToRegAndStack<[R1L, R2L, R3L], 8, 8>>, CCIfType<[i64], CCAssignToRegAndStack<[R1D, R2D, R3D], 8, 8>>, // The first 8 named vector arguments are passed in V24-V31. Sub-128 vectors diff --git a/llvm/test/CodeGen/SystemZ/call-zos-01.ll b/llvm/test/CodeGen/SystemZ/call-zos-01.ll index a6006035dcaa1..425cb0b897758 100644 --- a/llvm/test/CodeGen/SystemZ/call-zos-01.ll +++ b/llvm/test/CodeGen/SystemZ/call-zos-01.ll @@ -11,7 +11,7 @@ define i8 @call_char(){ ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,8(5) ; CHECK-NEXT: lg 5,0(5) -; CHECK-NEXT: lghi 1,8 +; CHECK-NEXT: lhi 1,8 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -30,7 +30,7 @@ define i16 @call_short() { ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,24(5) ; CHECK-NEXT: lg 5,16(5) -; CHECK-NEXT: lghi 1,16 +; CHECK-NEXT: lhi 1,16 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -50,8 +50,8 @@ define i32 @call_int() { ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,40(5) ; CHECK-NEXT: lg 5,32(5) -; CHECK-NEXT: lghi 1,32 -; CHECK-NEXT: lghi 2,33 +; CHECK-NEXT: lhi 1,32 +; CHECK-NEXT: lhi 2,33 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -114,8 +114,8 @@ define i64 @call_integrals() { ; CHECK-NEXT: lg 6,88(5) ; CHECK-NEXT: lg 5,80(5) ; CHECK-NEXT: lghi 1,64 -; CHECK-NEXT: lghi 2,32 -; CHECK-NEXT: lghi 3,16 +; CHECK-NEXT: lhi 2,32 +; CHECK-NEXT: lhi 3,16 ; CHECK-NEXT: mvghi 2200(4),128 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 @@ -129,7 +129,7 @@ entry: define signext i8 @pass_char(i8 signext %arg) { ; CHECK-LABEL: pass_char DS 0H -; CHECK: lgr 3,1 +; CHECK: lgfr 3,1 ; CHECK-NEXT: b 2(7) entry: ret i8 %arg @@ -137,7 +137,7 @@ entry: define signext i16 @pass_short(i16 signext %arg) { ; CHECK-LABEL: pass_short DS 0H -; CHECK: lgr 3,1 +; CHECK: lgfr 3,1 ; CHECK-NEXT: b 2(7) entry: ret i16 %arg @@ -145,7 +145,7 @@ entry: define signext i32 @pass_int(i32 signext %arg0, i32 signext %arg1) { ; CHECK-LABEL: pass_int DS 0H -; CHECK: lgr 3,2 +; CHECK: lgfr 3,2 ; CHECK-NEXT: b 2(7) entry: ret i32 %arg1 @@ -164,8 +164,8 @@ entry: define signext i64 @pass_integrals0(i64 signext %arg0, i32 signext %arg1, i16 signext %arg2, i64 signext %arg3) { ; CHECK-LABEL: pass_integrals0 DS 0H -; CHECK: ag 2,2200(4) -; CHECK-NEXT: lgr 3,2 +; CHECK: lgfr 3,2 +; CHECK-NEXT: ag 3,2200(4) ; CHECK-NEXT: b 2(7) entry: %N = sext i32 %arg1 to i64 diff --git a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll index 3bcc583adec7f..a02c41d298a2c 100644 --- a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll +++ b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll @@ -287,10 +287,10 @@ define void @call_vec_double_vararg_straddle(<2 x double> %v) { ; CHECK-NEXT: aghi 4,-192 ; CHECK-NEXT: *FENCE ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H -; CHECK-NEXT: lg 0,2392(4) +; CHECK-NEXT: l 0,2396(4) ; CHECK-NEXT: lg 6,40(5) ; CHECK-NEXT: lg 5,32(5) -; CHECK-NEXT: stg 0,2200(4) +; CHECK-NEXT: st 0,2204(4) ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) diff --git a/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll b/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll index cae54638c3191..cec4490c0ac1b 100644 --- a/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll +++ b/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll @@ -307,7 +307,7 @@ entry: ; CHECK-NEXT: lg 5,16(5) ; CHECK-NEXT: stg 1,2216(4) ; CHECK-NEXT: stg 1,2208(4) -; CHECK-NEXT: lghi 1,5 +; CHECK-NEXT: lhi 1,5 ; CHECK-NEXT: stg 2,2200(4) ; CHECK-NEXT: lgr 3,2 ; CHECK-NEXT: basr 7,6 diff --git a/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll index a79e3b67ac3d1..c250c2114da29 100644 --- a/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll +++ b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll @@ -30,7 +30,7 @@ declare void @Caller(ptr noundef) ; CHECK: aghi 4,-192 ; CHECK: lg 1,24(5) ; CHECK: lg 2,32(5) -; CHECK: lgf 1,0(1) +; CHECK: l 1,0(1) ; CHECK: lg 6,48(5) ; CHECK: lg 5,40(5) ; CHECK: l 8,0(2) diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll index acbd624fd10ff..610c851798614 100644 --- a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll +++ b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll @@ -19,7 +19,7 @@ define void @fLargeOutArgArea() { ; CHECK-LABEL: L#EPM_fLargeOutArgArea_0 DS 0H ; CHECK: * Bit 1: 0 = Non-leaf function ; CHECK: * Bit 2: 0 = Does not use alloca -; CHECK: DC XL4'00000260' +; CHECK: DC XL4'00000220' ; CHECK: fLargeOutArgArea DS 0H %1 = load [33 x i32], ptr @GlobLargeS, align 4 call void @fLargeParm([33 x i32] inreg %1) >From b17680d9a079fb5fff0c62f97928a34c1d3a7fe4 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Tue, 28 Jul 2026 16:36:06 -0400 Subject: [PATCH 4/7] [SystemZ] Fix XPLINK64 i32 arg passing: promote to i64 to fill full GPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rule CCAssignToRegAndStack<[R1L, R2L, R3L], 8, 8> writes only the 32-bit sub-register of each GPR for i32 arguments. Callees compiled by other compilers (e.g. xlc / @@CLASSB) read the full 64-bit register, so the upper 32 bits being undefined produces wrong results — concretely, calling @@CLASSB via isinf() returns 1 instead of 0 for a finite float because the upper 32 bits of R3 are garbage when only R3L was written. Fix: replace CCAssignToRegAndStack<[R1L, R2L, R3L], 8, 8> with CCPromoteToType<i64> for i32 arguments, placed after the i128/pointer/ vararg rules and before the i64 register-assignment rule. This promotes i32 to i64 (LocInfo=AExt when no signext/zeroext flag is present), so the caller emits ANY_EXTEND and writes the full 64-bit register R1D/R2D/R3D. On the callee side, LocInfo=AExt causes a TRUNCATE back to i32, leaving the callee's view of the value correct at its natural width. The downstream CCAssignToRegAndStack<[R1D, R2D, R3D], 8, 8> then assigns the promoted i64 to the full 64-bit registers, matching the 8-byte stack slot required by XPLINK64. Update all affected lit tests: - call-zos-01.ll: lhi->lghi, lgfr->lgr, reorder ag/lgr in pass_integrals0 - call-zos-vararg.ll: l->lg, st->stg - mixed-ptr-sizes.ll: lhi->lghi - zos-ada-relocations.ll: l->lgf (sign-extending i32 load) - zos-ppa1-argarea.ll: DSA size 0x220->0x260 (i32 stack slots now 8 bytes) --- llvm/lib/Target/SystemZ/SystemZCallingConv.td | 12 +++++++--- llvm/test/CodeGen/SystemZ/call-zos-01.ll | 22 +++++++++---------- llvm/test/CodeGen/SystemZ/call-zos-vararg.ll | 4 ++-- llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll | 2 +- .../CodeGen/SystemZ/zos-ada-relocations.ll | 2 +- llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll | 2 +- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td index c886040bf38d1..6518b098d55bf 100644 --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -240,11 +240,17 @@ def CC_SystemZ_XPLINK64 : CallingConv<[ // so we have to use a custom handler. CCIfType<[i64], CCCustom<"CC_SystemZ_I128Indirect">>, // The first 3 integer arguments are passed in registers R1-R3. - // i32 uses the low-word sub-registers but occupies a full 8-byte slot, - // matching the XPLINK64 requirement that each parameter area word is 8 bytes. + // i32 is promoted to i64 (AExt) unconditionally so that the full 64-bit + // register (R1D/R2D/R3D) is written by the caller. Callees compiled by + // other compilers (e.g. xlc / @@CLASSB) read the full GPR; writing only + // the 32-bit sub-register leaves the upper half undefined and produces + // wrong results (e.g. isinf returning 1 instead of 0). + // Note: CCPromoteToType sets LocInfo=AExt when no signext/zeroext flag is + // present, so formal-arg lowering truncates back to i32 via TRUNCATE, + // leaving the callee's view of the value correct at its natural width. // The rest will be passed in the user area. The address offset of the user // area can be found in register R4D. - CCIfType<[i32], CCAssignToRegAndStack<[R1L, R2L, R3L], 8, 8>>, + CCIfType<[i32], CCPromoteToType<i64>>, CCIfType<[i64], CCAssignToRegAndStack<[R1D, R2D, R3D], 8, 8>>, // The first 8 named vector arguments are passed in V24-V31. Sub-128 vectors diff --git a/llvm/test/CodeGen/SystemZ/call-zos-01.ll b/llvm/test/CodeGen/SystemZ/call-zos-01.ll index 425cb0b897758..a6006035dcaa1 100644 --- a/llvm/test/CodeGen/SystemZ/call-zos-01.ll +++ b/llvm/test/CodeGen/SystemZ/call-zos-01.ll @@ -11,7 +11,7 @@ define i8 @call_char(){ ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,8(5) ; CHECK-NEXT: lg 5,0(5) -; CHECK-NEXT: lhi 1,8 +; CHECK-NEXT: lghi 1,8 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -30,7 +30,7 @@ define i16 @call_short() { ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,24(5) ; CHECK-NEXT: lg 5,16(5) -; CHECK-NEXT: lhi 1,16 +; CHECK-NEXT: lghi 1,16 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -50,8 +50,8 @@ define i32 @call_int() { ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H ; CHECK-NEXT: lg 6,40(5) ; CHECK-NEXT: lg 5,32(5) -; CHECK-NEXT: lhi 1,32 -; CHECK-NEXT: lhi 2,33 +; CHECK-NEXT: lghi 1,32 +; CHECK-NEXT: lghi 2,33 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) @@ -114,8 +114,8 @@ define i64 @call_integrals() { ; CHECK-NEXT: lg 6,88(5) ; CHECK-NEXT: lg 5,80(5) ; CHECK-NEXT: lghi 1,64 -; CHECK-NEXT: lhi 2,32 -; CHECK-NEXT: lhi 3,16 +; CHECK-NEXT: lghi 2,32 +; CHECK-NEXT: lghi 3,16 ; CHECK-NEXT: mvghi 2200(4),128 ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 @@ -129,7 +129,7 @@ entry: define signext i8 @pass_char(i8 signext %arg) { ; CHECK-LABEL: pass_char DS 0H -; CHECK: lgfr 3,1 +; CHECK: lgr 3,1 ; CHECK-NEXT: b 2(7) entry: ret i8 %arg @@ -137,7 +137,7 @@ entry: define signext i16 @pass_short(i16 signext %arg) { ; CHECK-LABEL: pass_short DS 0H -; CHECK: lgfr 3,1 +; CHECK: lgr 3,1 ; CHECK-NEXT: b 2(7) entry: ret i16 %arg @@ -145,7 +145,7 @@ entry: define signext i32 @pass_int(i32 signext %arg0, i32 signext %arg1) { ; CHECK-LABEL: pass_int DS 0H -; CHECK: lgfr 3,2 +; CHECK: lgr 3,2 ; CHECK-NEXT: b 2(7) entry: ret i32 %arg1 @@ -164,8 +164,8 @@ entry: define signext i64 @pass_integrals0(i64 signext %arg0, i32 signext %arg1, i16 signext %arg2, i64 signext %arg3) { ; CHECK-LABEL: pass_integrals0 DS 0H -; CHECK: lgfr 3,2 -; CHECK-NEXT: ag 3,2200(4) +; CHECK: ag 2,2200(4) +; CHECK-NEXT: lgr 3,2 ; CHECK-NEXT: b 2(7) entry: %N = sext i32 %arg1 to i64 diff --git a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll index a02c41d298a2c..3bcc583adec7f 100644 --- a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll +++ b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll @@ -287,10 +287,10 @@ define void @call_vec_double_vararg_straddle(<2 x double> %v) { ; CHECK-NEXT: aghi 4,-192 ; CHECK-NEXT: *FENCE ; CHECK-NEXT: L#end_of_prologue{{[0-9]+}} DS 0H -; CHECK-NEXT: l 0,2396(4) +; CHECK-NEXT: lg 0,2392(4) ; CHECK-NEXT: lg 6,40(5) ; CHECK-NEXT: lg 5,32(5) -; CHECK-NEXT: st 0,2204(4) +; CHECK-NEXT: stg 0,2200(4) ; CHECK-NEXT: basr 7,6 ; CHECK-NEXT: bcr 0,0 ; CHECK-NEXT: lg 7,2072(4) diff --git a/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll b/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll index cec4490c0ac1b..cae54638c3191 100644 --- a/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll +++ b/llvm/test/CodeGen/SystemZ/mixed-ptr-sizes.ll @@ -307,7 +307,7 @@ entry: ; CHECK-NEXT: lg 5,16(5) ; CHECK-NEXT: stg 1,2216(4) ; CHECK-NEXT: stg 1,2208(4) -; CHECK-NEXT: lhi 1,5 +; CHECK-NEXT: lghi 1,5 ; CHECK-NEXT: stg 2,2200(4) ; CHECK-NEXT: lgr 3,2 ; CHECK-NEXT: basr 7,6 diff --git a/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll index c250c2114da29..a79e3b67ac3d1 100644 --- a/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll +++ b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll @@ -30,7 +30,7 @@ declare void @Caller(ptr noundef) ; CHECK: aghi 4,-192 ; CHECK: lg 1,24(5) ; CHECK: lg 2,32(5) -; CHECK: l 1,0(1) +; CHECK: lgf 1,0(1) ; CHECK: lg 6,48(5) ; CHECK: lg 5,40(5) ; CHECK: l 8,0(2) diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll index 610c851798614..acbd624fd10ff 100644 --- a/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll +++ b/llvm/test/CodeGen/SystemZ/zos-ppa1-argarea.ll @@ -19,7 +19,7 @@ define void @fLargeOutArgArea() { ; CHECK-LABEL: L#EPM_fLargeOutArgArea_0 DS 0H ; CHECK: * Bit 1: 0 = Non-leaf function ; CHECK: * Bit 2: 0 = Does not use alloca -; CHECK: DC XL4'00000220' +; CHECK: DC XL4'00000260' ; CHECK: fLargeOutArgArea DS 0H %1 = load [33 x i32], ptr @GlobLargeS, align 4 call void @fLargeParm([33 x i32] inreg %1) >From b012f91c040689c8160e9225b77b4037c7c0ad00 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Wed, 29 Jul 2026 14:03:38 -0400 Subject: [PATCH 5/7] [XPLINK64] Extend sub-64-bit integers at variadic call sites Named XPLINK64 arguments intentionally omit signext/zeroext to interoperate with xlc, which leaves upper GPR bits unspecified. However, variadic arguments are different: the callee has no prototype and reads the full 64-bit GPR, so the upper bits must be clean. Fix classifyArgumentType() to emit getExtend() instead of getDirect() for promotable integer types when IsNamedArg is false (i.e. variadic arguments). This fixes a miscompile visible in MultiSource/Benchmarks/Ptrdist/ks where 'int' values passed to printf with '%lu' printed garbage upper bits, e.g. 'Net 343597383682 cut.' instead of 'Net 2 cut.'. --- clang/lib/CodeGen/Targets/SystemZ.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index d2be96131c4db..c43bc3a500e72 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -823,15 +823,22 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), RAA == CGCXXABI::RAA_DirectInMemory); - // The XPLINK64 ABI does not mandate any widening of integer arguments; + // The XPLINK64 ABI does not mandate any widening of named integer arguments; // arguments are passed at their natural width with no sign- or zero-extension // guarantee. Only return values are required to be widened (per the z/OS // Language Environment Vendor Interfaces spec). Other compilers (e.g. xlc) // leave the upper bits of an argument register unspecified, so emitting - // signext/zeroext on parameters would produce incorrect code when + // signext/zeroext on named parameters would produce incorrect code when // interoperating with xlc. - if (isPromotableIntegerTypeForABI(Ty)) - return ABIArgInfo::getDirect(CGT.ConvertType(Ty)); + // + // However, variadic arguments are different: the callee has no prototype for + // them and reads a full 64-bit GPR, so the upper bits must be clean. + // Extend variadic integer arguments to 64 bits. + if (isPromotableIntegerTypeForABI(Ty)) { + if (IsNamedArg) + return ABIArgInfo::getDirect(CGT.ConvertType(Ty)); + return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty)); + } // For non-C calling conventions, compound types passed by address copy. if ((CallConv != llvm::CallingConv::C) && isCompoundType(Ty)) >From 3762fc467a9192d317a97b5d69a4702cd30753a3 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Thu, 30 Jul 2026 16:14:33 -0400 Subject: [PATCH 6/7] [SystemZ] Fix XPLINK64 i32 call-arg AExt producing LGF at -O0 When convertValVTToLocVT emits ISD::ANY_EXTEND for an AExt CCValAssign and the value was spilled to a 32-bit stack slot (which happens at -O0), the SelectionDAG lowers the reload+extend on big-endian S390 as LGF (sign-extend load), not LLGF (zero-extend load). This corrupts negative int arguments: -1 is widened to 0xffffffffffffffff instead of the correct 0x00000000ffffffff required by XPLINK64. Fix: add an IsCallArg parameter to convertValVTToLocVT (default false). When IsCallArg=true and LocInfo==AExt, emit ZERO_EXTEND instead of ANY_EXTEND. Return-value paths keep ANY_EXTEND to avoid emitting unnecessary lgfr instructions. Also update the comment in clang/lib/CodeGen/Targets/SystemZ.cpp to better explain why named integer arguments use getDirect (no extend attribute) while variadic arguments use getExtend. --- clang/lib/CodeGen/Targets/SystemZ.cpp | 21 +++++++++++-------- .../Target/SystemZ/SystemZISelLowering.cpp | 16 ++++++++++++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index c43bc3a500e72..9ff394290c1e1 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -824,16 +824,19 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, RAA == CGCXXABI::RAA_DirectInMemory); // The XPLINK64 ABI does not mandate any widening of named integer arguments; - // arguments are passed at their natural width with no sign- or zero-extension - // guarantee. Only return values are required to be widened (per the z/OS - // Language Environment Vendor Interfaces spec). Other compilers (e.g. xlc) - // leave the upper bits of an argument register unspecified, so emitting - // signext/zeroext on named parameters would produce incorrect code when - // interoperating with xlc. + // other compilers (e.g. xlc) may leave the upper 32 bits of a GPR undefined + // when passing a sub-64-bit value. Emitting signext/zeroext on named + // parameters would cause clang-as-callee to add prologue sign/zero-extension + // that could conflict with an xlc caller that did not extend. // - // However, variadic arguments are different: the callee has no prototype for - // them and reads a full 64-bit GPR, so the upper bits must be clean. - // Extend variadic integer arguments to 64 bits. + // The caller is still required to fill the full 64-bit register (per + // CCPromoteToType<i64> / AExt in the backend). Use getDirect so that + // no extend attribute appears in the IR; AExt is applied by the calling + // convention tables. + // + // Variadic arguments are different: the callee has no prototype and reads + // the full 64-bit GPR, so the upper bits must be clean. getExtend emits + // a zero-extend (for unsigned types) or sign-extend (for signed types). if (isPromotableIntegerTypeForABI(Ty)) { if (IsNamedArg) return ABIArgInfo::getDirect(CGT.ConvertType(Ty)); diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp index 42d100cd4f574..626d252855026 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1906,14 +1906,26 @@ static SDValue convertLocVTToValVT(SelectionDAG &DAG, const SDLoc &DL, // Value is a value of type VA.getValVT() that we need to copy into // the location described by VA. Return a copy of Value converted to // VA.getValVT(). The caller is responsible for handling indirect values. +// +// IsCallArg: true when converting an outgoing call argument. For AExt +// (any-extend), ZERO_EXTEND is used instead of ANY_EXTEND to prevent the +// backend from selecting LGF (sign-extend) when reloading a 32-bit value +// from a spill slot into a 64-bit register at -O0 on big-endian S390. +// For return values (IsCallArg=false) the original ANY_EXTEND is kept so +// that no unnecessary sign/zero-extension instruction is emitted. static SDValue convertValVTToLocVT(SelectionDAG &DAG, const SDLoc &DL, - CCValAssign &VA, SDValue Value) { + CCValAssign &VA, SDValue Value, + bool IsCallArg = false) { switch (VA.getLocInfo()) { case CCValAssign::SExt: return DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::ZExt: return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::AExt: + // For call arguments, use ZERO_EXTEND so that a spilled i32 is reloaded + // with LLGF (zero-extend) rather than LGF (sign-extend) at -O0. + if (IsCallArg) + return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value); return DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::BCvt: { assert(VA.getLocVT() == MVT::i64 || VA.getLocVT() == MVT::i128); @@ -2410,7 +2422,7 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI, } ArgValue = SpillSlot; } else - ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue); + ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue, /*IsCallArg=*/true); if (VA.isRegLoc()) { // In XPLINK64, for the 128-bit vararg case, ArgValue is bitcasted to a >From cd4e90cfcb082d5a2955e77534e862094a2494b2 Mon Sep 17 00:00:00 2001 From: Zibi Sarbinowski <[email protected]> Date: Mon, 3 Aug 2026 22:37:53 -0400 Subject: [PATCH 7/7] Revert "[SystemZ] Fix XPLINK64 i32 call-arg AExt producing LGF at -O0" This reverts commit 3762fc467a9192d317a97b5d69a4702cd30753a3. --- clang/lib/CodeGen/Targets/SystemZ.cpp | 21 ++++++++----------- .../Target/SystemZ/SystemZISelLowering.cpp | 16 ++------------ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 9ff394290c1e1..c43bc3a500e72 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -824,19 +824,16 @@ ABIArgInfo ZOSXPLinkABIInfo::classifyArgumentType(QualType Ty, bool IsNamedArg, RAA == CGCXXABI::RAA_DirectInMemory); // The XPLINK64 ABI does not mandate any widening of named integer arguments; - // other compilers (e.g. xlc) may leave the upper 32 bits of a GPR undefined - // when passing a sub-64-bit value. Emitting signext/zeroext on named - // parameters would cause clang-as-callee to add prologue sign/zero-extension - // that could conflict with an xlc caller that did not extend. + // arguments are passed at their natural width with no sign- or zero-extension + // guarantee. Only return values are required to be widened (per the z/OS + // Language Environment Vendor Interfaces spec). Other compilers (e.g. xlc) + // leave the upper bits of an argument register unspecified, so emitting + // signext/zeroext on named parameters would produce incorrect code when + // interoperating with xlc. // - // The caller is still required to fill the full 64-bit register (per - // CCPromoteToType<i64> / AExt in the backend). Use getDirect so that - // no extend attribute appears in the IR; AExt is applied by the calling - // convention tables. - // - // Variadic arguments are different: the callee has no prototype and reads - // the full 64-bit GPR, so the upper bits must be clean. getExtend emits - // a zero-extend (for unsigned types) or sign-extend (for signed types). + // However, variadic arguments are different: the callee has no prototype for + // them and reads a full 64-bit GPR, so the upper bits must be clean. + // Extend variadic integer arguments to 64 bits. if (isPromotableIntegerTypeForABI(Ty)) { if (IsNamedArg) return ABIArgInfo::getDirect(CGT.ConvertType(Ty)); diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp index 626d252855026..42d100cd4f574 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1906,26 +1906,14 @@ static SDValue convertLocVTToValVT(SelectionDAG &DAG, const SDLoc &DL, // Value is a value of type VA.getValVT() that we need to copy into // the location described by VA. Return a copy of Value converted to // VA.getValVT(). The caller is responsible for handling indirect values. -// -// IsCallArg: true when converting an outgoing call argument. For AExt -// (any-extend), ZERO_EXTEND is used instead of ANY_EXTEND to prevent the -// backend from selecting LGF (sign-extend) when reloading a 32-bit value -// from a spill slot into a 64-bit register at -O0 on big-endian S390. -// For return values (IsCallArg=false) the original ANY_EXTEND is kept so -// that no unnecessary sign/zero-extension instruction is emitted. static SDValue convertValVTToLocVT(SelectionDAG &DAG, const SDLoc &DL, - CCValAssign &VA, SDValue Value, - bool IsCallArg = false) { + CCValAssign &VA, SDValue Value) { switch (VA.getLocInfo()) { case CCValAssign::SExt: return DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::ZExt: return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::AExt: - // For call arguments, use ZERO_EXTEND so that a spilled i32 is reloaded - // with LLGF (zero-extend) rather than LGF (sign-extend) at -O0. - if (IsCallArg) - return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value); return DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Value); case CCValAssign::BCvt: { assert(VA.getLocVT() == MVT::i64 || VA.getLocVT() == MVT::i128); @@ -2422,7 +2410,7 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI, } ArgValue = SpillSlot; } else - ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue, /*IsCallArg=*/true); + ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue); if (VA.isRegLoc()) { // In XPLINK64, for the 128-bit vararg case, ArgValue is bitcasted to a _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
