[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