https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/206833
>From a0b48828f1a4b2b3709ba60b8ff129d97dbc62ab 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(), return getDirect() instead of getExtend() for promotable integer types. This omits the signext/ zeroext attribute from LLVM IR parameters, so the backend emits no extension instruction for incoming arguments (lgr plain copy). - classifyReturnType() is unchanged: return values still use getExtend() because the spec does mandate extension there (lgfr/llgfr emitted). - The backend CC_XPLINK_Promote_i32 custom handler, SystemZCCState class, and isFormalArgLowering machinery are removed; the simple CCIfExtend<CCPromoteToType<i64>> rule is restored in CC_SystemZ_XPLINK64 since IR parameters no longer carry extension flags. Tests updated: - clang/test/CodeGen/SystemZ/zos-abi.c: parameter signatures no longer carry signext/zeroext; return types still do. - llvm/test/CodeGen/SystemZ/call-zos-01.ll: pass_char/short/int now emit lgr (plain copy) instead of lgfr; pass_integrals0 instruction order updated to match. - llvm/test/CodeGen/SystemZ/call-zos-vararg.ll: vararg integral call site emits lg + direct stg, no llgfr zero-extension. --- clang/lib/CodeGen/Targets/SystemZ.cpp | 11 ++++++++-- clang/test/CodeGen/SystemZ/zos-abi.c | 22 +++++++++---------- clang/test/CodeGen/pragma-export.cpp | 8 +++---- llvm/lib/Target/SystemZ/SystemZCallingConv.td | 1 + 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 37f6b3017f776..84ac63f0f0066 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -822,9 +822,16 @@ 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. + // Promotable integer arguments are passed without extension attributes. + // The XPLINK64 ABI specification mandates that scalar values are widened to + // 64 bits only for *return* values, not for arguments; other compilers (e.g. + // xlc) do not guarantee the upper bits are sign- or zero-extended on the + // caller side. Omitting signext/zeroext here ensures the backend does not + // emit a redundant extension instruction on the callee side and avoids + // incorrect code when interoperating with xlc. Return values still use + // getExtend() because the spec does mandate extension there. 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 d7843d9bee089..0eb0e03b49e9d 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 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 %{{.*}}) #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; } 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/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td index 69202e3fcbc57..df5c02917b16b 100644 --- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td +++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td @@ -216,6 +216,7 @@ 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>>>, + // 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(). _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
