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

Reply via email to