https://github.com/zibi2 updated 
https://github.com/llvm/llvm-project/pull/206833

>From 41df5e708ce8c9acb07a936190c0de58d5e72cad Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <[email protected]>
Date: Tue, 30 Jun 2026 16:25:39 -0400
Subject: [PATCH] [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 getNoExtend() for
  sub-64-bit promotable integer types.  This preserves normal C integer
  promotion (8/16-bit widening to 32 bits) but omits signext/zeroext
  from LLVM IR parameters, so the backend emits no 32->64-bit extension
  instruction for incoming arguments (lgr plain copy).  64-bit types
  (long, unsigned long) are already register-width and pass via
  getDirect() unchanged.
- 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: sub-64-bit integer parameters
  now carry noext; 64-bit and non-integer parameters unchanged; return
  types still carry signext/zeroext.
- clang/test/CodeGen/pragma-export.cpp: i32 parameters updated to noext.
---
 clang/lib/CodeGen/Targets/SystemZ.cpp         | 15 ++++++++++---
 clang/test/CodeGen/SystemZ/zos-abi.c          | 22 +++++++++----------
 clang/test/CodeGen/pragma-export.cpp          |  8 +++----
 .../include/llvm/Analysis/TargetLibraryInfo.h | 11 +++++++---
 4 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 37f6b3017f776..03e92478fcac5 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -822,9 +822,18 @@ 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.
-  if (isPromotableIntegerTypeForABI(Ty))
-    return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty));
+  // Sub-64-bit integer arguments undergo normal C integer promotion (8/16-bit
+  // types are widened to 32 bits) but are NOT sign- or zero-extended to 64
+  // bits.  The XPLINK64 ABI specification mandates 64-bit widening only for
+  // return values; other compilers (e.g. xlc) leave the upper 32 bits of an
+  // argument register unspecified, so emitting signext/zeroext on parameters
+  // would produce incorrect code when interoperating with xlc.
+  // 64-bit types (long, unsigned long) are already register-width and are
+  // passed unmodified via getDirect().
+  if (isPromotableIntegerTypeForABI(Ty) &&
+      getContext().getTypeSize(Ty) < 64)
+    return ABIArgInfo::getNoExtend(
+        cast<llvm::IntegerType>(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 d7843d9bee089..03bbba7dc2d63 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 noext %{{.*}})
 
 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 noext %{{.*}})
 
 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 noext %{{.*}})
 
 short pass_short(short arg) { return arg; }
-// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}})
+// CHECK-LABEL: define signext i16 @pass_short(i16 noext %{{.*}})
 
 int pass_int(int arg) { return arg; }
-// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}})
+// CHECK-LABEL: define signext i32 @pass_int(i32 noext %{{.*}})
 
 long pass_long(long arg) { return arg; }
-// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}})
+// CHECK-LABEL: define signext 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 noext %{{.*}})
 
 #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 noext %{{.*}})
 
 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 noext %{{.*}})
 
 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 noext %{{.*}})
 
 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; }
diff --git a/clang/test/CodeGen/pragma-export.cpp 
b/clang/test/CodeGen/pragma-export.cpp
index 531afbd659234..d977e4c2e9940 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 noext noundef %0)
+// CHECK: define hidden void @_Z2f2ii(i32 noext noundef %0, i32 noext 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 noext 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 noext 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()) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to