[llvm] [SPARC] Fix va_arg of long double on the 32-bit ABI (PR #228199)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 11:56:10 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-sparc
Author: Imre Kaloz (kaloz)
<details>
<summary>Changes</summary>
The 32-bit ABI passes a quad-precision value by invisible reference,
so its variable-argument slot holds a pointer to the value. Lowering
advanced the list by the value's size and loaded it from the slot, so
va_arg(ap, long double) read the pointer and the three words after it
as a quad, leaving the list twelve bytes past the next argument.
Advance by a word and load through the pointer instead; V9 passes
quads directly.
clang lowers va_arg itself for SPARC, so this affects IR va_arg from
other producers.
Signed-off-by: Imre Kaloz <kaloz@<!-- -->kernel.org>
---
Full diff: https://github.com/llvm/llvm-project/pull/228199.diff
2 Files Affected:
- (modified) llvm/lib/Target/Sparc/SparcISelLowering.cpp (+21-8)
- (modified) llvm/test/CodeGen/SPARC/varargs-v8.ll (+36)
``````````diff
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.cpp b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
index 66638f599559a..2a7ab4d858676 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.cpp
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
@@ -2738,7 +2738,8 @@ static SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG,
MachinePointerInfo(SV));
}
-static SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) {
+static SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG,
+ const SparcSubtarget &Subtarget) {
SDNode *Node = Op.getNode();
EVT VT = Node->getValueType(0);
SDValue InChain = Node->getOperand(0);
@@ -2748,18 +2749,29 @@ static SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) {
SDLoc DL(Node);
SDValue VAList =
DAG.getLoad(PtrVT, DL, InChain, VAListPtr, MachinePointerInfo(SV));
+ // The 32-bit ABI passes a quad-precision value by invisible reference, so its
+ // slot holds a pointer to the value rather than the value itself. V9 passes
+ // it directly.
+ bool Indirect = !Subtarget.is64Bit() && VT == MVT::f128;
// Increment the pointer, VAList, to the next vaarg.
+ unsigned ArgSize =
+ (Indirect ? PtrVT.getFixedSizeInBits() : VT.getFixedSizeInBits()) / 8;
SDValue NextPtr = DAG.getNode(ISD::ADD, DL, PtrVT, VAList,
- DAG.getIntPtrConstant(VT.getSizeInBits()/8,
- DL));
+ DAG.getIntPtrConstant(ArgSize, DL));
// Store the incremented VAList to the legalized pointer.
InChain = DAG.getStore(VAList.getValue(1), DL, NextPtr, VAListPtr,
MachinePointerInfo(SV));
- // Load the actual argument out of the pointer VAList.
// We can't count on greater alignment than the word size.
- return DAG.getLoad(
- VT, DL, InChain, VAList, MachinePointerInfo(),
- Align(std::min(PtrVT.getFixedSizeInBits(), VT.getFixedSizeInBits()) / 8));
+ Align Alignment =
+ Align(std::min(PtrVT.getFixedSizeInBits(), VT.getFixedSizeInBits()) / 8);
+ if (Indirect) {
+ // Load the pointer out of the slot, then the value it addresses.
+ SDValue Ptr = DAG.getLoad(PtrVT, DL, InChain, VAList, MachinePointerInfo());
+ return DAG.getLoad(VT, DL, Ptr.getValue(1), Ptr, MachinePointerInfo(),
+ Alignment);
+ }
+ // Load the actual argument out of the pointer VAList.
+ return DAG.getLoad(VT, DL, InChain, VAList, MachinePointerInfo(), Alignment);
}
static SDValue LowerSTACKADDRESS(SDValue Op, SelectionDAG &DAG,
@@ -3212,7 +3224,8 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const {
case ISD::SELECT_CC:
return LowerSELECT_CC(Op, DAG, *this, hasHardQuad, isV9, is64Bit);
case ISD::VASTART: return LowerVASTART(Op, DAG, *this);
- case ISD::VAARG: return LowerVAARG(Op, DAG);
+ case ISD::VAARG:
+ return LowerVAARG(Op, DAG, *Subtarget);
case ISD::DYNAMIC_STACKALLOC: return LowerDYNAMIC_STACKALLOC(Op, DAG,
Subtarget);
case ISD::STACKADDRESS:
diff --git a/llvm/test/CodeGen/SPARC/varargs-v8.ll b/llvm/test/CodeGen/SPARC/varargs-v8.ll
index 02351cd639fc9..cb67a00746ffd 100644
--- a/llvm/test/CodeGen/SPARC/varargs-v8.ll
+++ b/llvm/test/CodeGen/SPARC/varargs-v8.ll
@@ -22,3 +22,39 @@ entry:
%add3 = add nsw i32 %1, %conv1
ret i32 %add3
}
+
+;; The 32-bit ABI passes a quad-precision value by invisible reference, so the
+;; slot holds a pointer: advance the list by a word, load the pointer, then load
+;; the value through it.
+define fp128 @test_f128(ptr %va) nounwind {
+; CHECK-LABEL: test_f128:
+; CHECK: ! %bb.0: ! %entry
+; CHECK-NEXT: save %sp, -120, %sp
+; CHECK-NEXT: add %i0, 4, %i1
+; CHECK-NEXT: st %i1, [%fp+-4]
+; CHECK-NEXT: ld [%i0], %i0
+; CHECK-NEXT: ld [%i0+4], %i1
+; CHECK-NEXT: add %fp, -24, %i2
+; CHECK-NEXT: or %i2, 4, %i2
+; CHECK-NEXT: st %i1, [%i2]
+; CHECK-NEXT: ld [%i0+12], %i1
+; CHECK-NEXT: add %fp, -16, %i2
+; CHECK-NEXT: or %i2, 4, %i2
+; CHECK-NEXT: st %i1, [%i2]
+; CHECK-NEXT: ld [%i0], %i1
+; CHECK-NEXT: st %i1, [%fp+-24]
+; CHECK-NEXT: ld [%i0+8], %i0
+; CHECK-NEXT: ld [%fp+64], %i1
+; CHECK-NEXT: st %i0, [%fp+-16]
+; CHECK-NEXT: ldd [%fp+-24], %f0
+; CHECK-NEXT: ldd [%fp+-16], %f2
+; CHECK-NEXT: std %f0, [%i1]
+; CHECK-NEXT: std %f2, [%i1+8]
+; CHECK-NEXT: ret
+; CHECK-NEXT: restore
+entry:
+ %va.addr = alloca ptr, align 4
+ store ptr %va, ptr %va.addr, align 4
+ %0 = va_arg ptr %va.addr, fp128
+ ret fp128 %0
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/228199
More information about the llvm-commits
mailing list