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
