[llvm] [SystemZ] Fix signext attribute handling in XPLINK64 calling convention (PR #206833)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 13:50:59 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-systemz
Author: Zibi Sarbinowski (zibi2)
<details>
<summary>Changes</summary>
For z/OS XPLINK64, signext/zeroext return values (i8/i16/i32) were incorrectly emitting lgr (plain 64-bit copy) instead of lgfr (sign-extend 32→64). Similarly, signext args passed as varargs were emitting lgfr instead of llgfr (zero-extend 32→64).
Fixes:
1. Replace CCPromoteToType<i64> with a custom handler CC_XPLINK_Promote_i32 in CC_SystemZ_XPLINK64 (.td) so SExt/ZExt flags are preserved for call operands and return values, producing the correct lgfr/llgfr instruction.
2. Add SystemZCCState class (with isFormalArgLowering tracking) to SystemZCallingConv.h and use it in LowerFormalArguments so that formal argument lowering uses AExt, maintaining ABI interoperability with xlc.
3. In CC_XPLINK_Promote_i32, vararg positions always use ZExt (llgfr) for ABI compat with xlc: the callee receives varargs with AExt, so the caller must zero-extend to avoid upper-bit contamination.
---
Full diff: https://github.com/llvm/llvm-project/pull/206833.diff
5 Files Affected:
- (modified) llvm/lib/Target/SystemZ/SystemZCallingConv.h (+40)
- (modified) llvm/lib/Target/SystemZ/SystemZCallingConv.td (+5-3)
- (modified) llvm/lib/Target/SystemZ/SystemZISelLowering.cpp (+3-1)
- (modified) llvm/test/CodeGen/SystemZ/call-zos-01.ll (+6-6)
- (modified) llvm/test/CodeGen/SystemZ/call-zos-vararg.ll (+4-1)
``````````diff
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.h b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
index f5ffbf5c04d60..32b40a851261d 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.h
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
@@ -29,6 +29,19 @@ namespace SystemZ {
extern const MCPhysReg XPLINK64ArgFPRs[XPLINK64NumArgFPRs];
} // end namespace SystemZ
+class SystemZCCState : public CCState {
+private:
+ bool IsFormalArgLowering = false;
+
+public:
+ SystemZCCState(CallingConv::ID CC, bool isVarArg, MachineFunction &MF,
+ SmallVectorImpl<CCValAssign> &locs, LLVMContext &C)
+ : CCState(CC, isVarArg, MF, locs, C) {}
+
+ bool isFormalArgLowering() const { return IsFormalArgLowering; }
+ void setIsFormalArgLowering() { IsFormalArgLowering = true; }
+};
+
// Handle i128 argument types. These need to be passed by implicit
// reference. This could be as simple as the following .td line:
// CCIfType<[i128], CCPassIndirect<i64>>,
@@ -156,6 +169,33 @@ inline bool CC_XPLINK64_Allocate128BitVararg(unsigned &ValNo, MVT &ValVT,
return false;
}
+inline bool CC_XPLINK_Promote_i32(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
+ CCValAssign::LocInfo &LocInfo,
+ ISD::ArgFlagsTy &ArgFlags, CCState &State) {
+
+ assert(ValVT.isInteger() && ValVT == MVT::i32 && "Expected MVT:32 as ValVT");
+ LocVT = MVT::i64;
+ // When lowering formal arguments, do not assume the caller has widened the
+ // value (ABI interoperability with xxlc). For args and return values,
+ // preserve SExt/ZExt so the correct extension instruction is emitted.
+ if (static_cast<SystemZCCState &>(State).isFormalArgLowering()) {
+ LocInfo = CCValAssign::AExt;
+ } else if (ArgFlags.isVarArg()) {
+ // For vararg args, always zero-extend for ABI interoperability with xxlc:
+ // the callee treats formal varargs with AExt (no sign/zero extension
+ // assumption), so the caller must zero-extend to avoid upper-bit garbage.
+ LocInfo = CCValAssign::ZExt;
+ } else {
+ if (ArgFlags.isSExt())
+ LocInfo = CCValAssign::SExt;
+ else if (ArgFlags.isZExt())
+ LocInfo = CCValAssign::ZExt;
+ else
+ LocInfo = CCValAssign::AExt;
+ }
+ return false;
+}
+
inline bool RetCC_SystemZ_Error(unsigned &, MVT &, MVT &,
CCValAssign::LocInfo &, ISD::ArgFlagsTy &,
CCState &) {
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
index 69202e3fcbc57..8d9c80d426128 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
@@ -213,9 +213,11 @@ 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>>>,
+ // The convention is that true integer arguments that are smaller
+ // than 64 bits should be marked as extended, but structures that
+ // are smaller than 64 bits shouldn't.
+ CCIfType<[i32], CCIfExtend<CCCustom<"CC_XPLINK_Promote_i32">>>,
+
// 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/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index e56a449cd455d..2dedc876b9ced 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -2038,7 +2038,9 @@ SDValue SystemZTargetLowering::LowerFormalArguments(
// Assign locations to all of the incoming arguments.
SmallVector<CCValAssign, 16> ArgLocs;
- CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
+ SystemZCCState CCInfo(CallConv, IsVarArg, MF, ArgLocs,
+ *DAG.getContext());
+ CCInfo.setIsFormalArgLowering();
CCInfo.AnalyzeFormalArguments(Ins, CC_SystemZ);
FuncInfo->setSizeOfFnParams(CCInfo.getStackSize());
diff --git a/llvm/test/CodeGen/SystemZ/call-zos-01.ll b/llvm/test/CodeGen/SystemZ/call-zos-01.ll
index 7a2fb3f2dfc49..f08ecb6a7f683 100644
--- a/llvm/test/CodeGen/SystemZ/call-zos-01.ll
+++ b/llvm/test/CodeGen/SystemZ/call-zos-01.ll
@@ -1,6 +1,6 @@
; Test the passing of scalar values in GPRs, FPRs in 64-bit calls on z/OS.
;
-; RUN: llc < %s -mtriple=s390x-ibm-zos -mcpu=z10 | FileCheck %s
+; RUN: llc < %s -mtriple=s390x-ibm-zos -mcpu=z10 | FileCheck %s
; CHECK-LABEL: call_char DS 0H
; CHECK: lghi 1,8
@@ -55,21 +55,21 @@ entry:
}
; CHECK-LABEL: pass_char DS 0H
-; CHECK: lgr 3,1
+; CHECK: lgfr 3,1
define signext i8 @pass_char(i8 signext %arg) {
entry:
ret i8 %arg
}
; CHECK-LABEL: pass_short DS 0H
-; CHECK: lgr 3,1
+; CHECK: lgfr 3,1
define signext i16 @pass_short(i16 signext %arg) {
entry:
ret i16 %arg
}
; CHECK-LABEL: pass_int DS 0H
-; CHECK: lgr 3,2
+; CHECK: lgfr 3,2
define signext i32 @pass_int(i32 signext %arg0, i32 signext %arg1) {
entry:
ret i32 %arg1
@@ -86,8 +86,8 @@ entry:
}
; 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)
define signext i64 @pass_integrals0(i64 signext %arg0, i32 signext %arg1, i16 signext %arg2, i64 signext %arg3) {
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 147fbe63f5af4..26daa75e754cb 100644
--- a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
+++ b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
@@ -257,9 +257,12 @@ define void @call_vec_double_vararg_straddle(<2 x double> %v) {
; CHECK-LABEL: call_vararg_integral0 DS 0H
; CHECK: stmg 6,7,1872(4)
; CHECK-NEXT: aghi 4,-192
-; 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: llgfr 0,0
+; CHECK-NEXT: llgfr 1,1
+; CHECK-NEXT: llgfr 2,2
; CHECK-NEXT: stg 0,2200(4)
; CHECK-NEXT: basr 7,6
; CHECK-NEXT: bcr 0,0
``````````
</details>
https://github.com/llvm/llvm-project/pull/206833
More information about the llvm-commits
mailing list