Skip to content

Conversation

koachan
Copy link
Contributor

@koachan koachan commented Oct 7, 2025

Pass and return long doubles indirectly, as specified in the psABI.
This continues the patch at https://reviews.llvm.org/D89130.

This should fix the issue at #41838.

Pass and return `long double`s indirectly, as specified in the psABI.
This continues the patch at https://reviews.llvm.org/D89130.

This should fix the issue at llvm#41838.
@llvmbot llvmbot added clang Clang issues not falling into any other category compiler-rt backend:Sparc clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. compiler-rt:builtins labels Oct 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 7, 2025

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-backend-sparc

Author: Koakuma (koachan)

Changes

Pass and return long doubles indirectly, as specified in the psABI.
This continues the patch at https://reviews.llvm.org/D89130.

This should fix the issue at #41838.


Full diff: https://github.com/llvm/llvm-project/pull/162226.diff

6 Files Affected:

  • (modified) clang/lib/Basic/Targets/Sparc.h (+7)
  • (modified) clang/lib/CodeGen/Targets/Sparc.cpp (+26-4)
  • (modified) compiler-rt/lib/builtins/CMakeLists.txt (+2-2)
  • (modified) compiler-rt/test/builtins/CMakeLists.txt (+1-1)
  • (modified) llvm/lib/Target/Sparc/SparcCallingConv.td (+2-2)
  • (modified) llvm/lib/Target/Sparc/SparcISelLowering.cpp (+10-9)
diff --git a/clang/lib/Basic/Targets/Sparc.h b/clang/lib/Basic/Targets/Sparc.h
index 3215e648ba6c3..acc27194c38ea 100644
--- a/clang/lib/Basic/Targets/Sparc.h
+++ b/clang/lib/Basic/Targets/Sparc.h
@@ -166,6 +166,13 @@ class LLVM_LIBRARY_VISIBILITY SparcV8TargetInfo : public SparcTargetInfo {
       PtrDiffType = SignedLong;
       break;
     }
+
+    // The SPARCv8 System V ABI has long double 128-bits in size, but 64-bit
+    // aligned.
+    LongDoubleWidth = 128;
+    LongDoubleAlign = 64;
+    LongDoubleFormat = &llvm::APFloat::IEEEquad();
+
     // Up to 32 bits (V8) or 64 bits (V9) are lock-free atomic, but we're
     // willing to do atomic ops on up to 64 bits.
     MaxAtomicPromoteWidth = 64;
diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp
index 38dbebdec2429..4259c8bbfdcae 100644
--- a/clang/lib/CodeGen/Targets/Sparc.cpp
+++ b/clang/lib/CodeGen/Targets/Sparc.cpp
@@ -26,6 +26,7 @@ class SparcV8ABIInfo : public DefaultABIInfo {
 
 private:
   ABIArgInfo classifyReturnType(QualType RetTy) const;
+  ABIArgInfo classifyArgumentType(QualType Ty) const;
   void computeInfo(CGFunctionInfo &FI) const override;
 };
 } // end anonymous namespace
@@ -33,12 +34,33 @@ class SparcV8ABIInfo : public DefaultABIInfo {
 
 ABIArgInfo
 SparcV8ABIInfo::classifyReturnType(QualType Ty) const {
+  if (Ty->isRealFloatingType() && getContext().getTypeSize(Ty) == 128)
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+
   if (Ty->isAnyComplexType()) {
-    return ABIArgInfo::getDirect();
-  }
-  else {
-    return DefaultABIInfo::classifyReturnType(Ty);
+    auto AI = ABIArgInfo::getDirect();
+    AI.setInReg(true);
+    return AI;
   }
+
+  return DefaultABIInfo::classifyReturnType(Ty);
+}
+
+ABIArgInfo SparcV8ABIInfo::classifyArgumentType(QualType Ty) const {
+  const BuiltinType *BT = Ty->getAs<BuiltinType>();
+  bool IsF128 = false;
+
+  if (Ty->isRealFloatingType() && getContext().getTypeSize(Ty) == 128)
+    IsF128 = true;
+
+  // FIXME not sure if redundant
+  if (BT && BT->getKind() == BuiltinType::LongDouble)
+    IsF128 = true;
+
+  if (IsF128)
+    return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+
+  return DefaultABIInfo::classifyArgumentType(Ty);
 }
 
 void SparcV8ABIInfo::computeInfo(CGFunctionInfo &FI) const {
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 6c226aa7d2d48..790bf5758f4a2 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -960,9 +960,9 @@ else ()
         list(APPEND BUILTIN_CFLAGS_${arch} -fomit-frame-pointer -DCOMPILER_RT_ARMHF_TARGET)
       endif()
 
-      # For RISCV32, we must force enable int128 for compiling long
+      # For RISCV32 and 32-bit SPARC, we must force enable int128 for compiling long
       # double routines.
-      if(COMPILER_RT_ENABLE_SOFTWARE_INT128 OR "${arch}" STREQUAL "riscv32")
+      if(COMPILER_RT_ENABLE_SOFTWARE_INT128 OR "${arch}" MATCHES "riscv32|sparc$" AND NOT CMAKE_COMPILER_IS_GNUCC)
         list(APPEND BUILTIN_CFLAGS_${arch} -fforce-enable-int128)
       endif()
 
diff --git a/compiler-rt/test/builtins/CMakeLists.txt b/compiler-rt/test/builtins/CMakeLists.txt
index 63f4c94605c90..5643bfc51ea81 100644
--- a/compiler-rt/test/builtins/CMakeLists.txt
+++ b/compiler-rt/test/builtins/CMakeLists.txt
@@ -44,7 +44,7 @@ foreach(arch ${BUILTIN_TEST_ARCH})
     string(REPLACE ";" " " BUILTINS_TEST_TARGET_CFLAGS "${BUILTINS_TEST_TARGET_CFLAGS}")
   endif()
 
-  if (COMPILER_RT_ENABLE_SOFTWARE_INT128 OR ${arch} STREQUAL "riscv32")
+  if (COMPILER_RT_ENABLE_SOFTWARE_INT128 OR "${arch}" MATCHES "riscv32|sparc$" AND NOT CMAKE_COMPILER_IS_GNUCC)
     list(APPEND BUILTINS_TEST_TARGET_CFLAGS -fforce-enable-int128)
     string(REPLACE ";" " " BUILTINS_TEST_TARGET_CFLAGS "${BUILTINS_TEST_TARGET_CFLAGS}")
   endif()
diff --git a/llvm/lib/Target/Sparc/SparcCallingConv.td b/llvm/lib/Target/Sparc/SparcCallingConv.td
index 8afd0a7fc09ad..55be696c14a78 100644
--- a/llvm/lib/Target/Sparc/SparcCallingConv.td
+++ b/llvm/lib/Target/Sparc/SparcCallingConv.td
@@ -24,8 +24,8 @@ def CC_Sparc32 : CallingConv<[
   // As are v2i32 arguments (this would be the default behavior for
   // v2i32 if it wasn't allocated to the IntPair register-class)
   CCIfType<[v2i32], CCCustom<"CC_Sparc_Assign_Split_64">>,
-
-
+  // f128 arguments are passed indirectly.
+  CCIfType<[f128], CCPassIndirect<i32>>,
   // Alternatively, they are assigned to the stack in 4-byte aligned units.
   CCAssignToStack<4, 4>
 ]>;
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.cpp b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
index a1607097af1ef..6ce636f470896 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.cpp
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.cpp
@@ -554,20 +554,19 @@ SDValue SparcTargetLowering::LowerFormalArguments_32(
       continue;
     }
 
-    int FI = MF.getFrameInfo().CreateFixedObject(4,
-                                                 Offset,
-                                                 true);
-    SDValue FIPtr = DAG.getFrameIndex(FI, PtrVT);
-    SDValue Load ;
+    int FI;
     if (VA.getValVT() == MVT::i32 || VA.getValVT() == MVT::f32) {
-      Load = DAG.getLoad(VA.getValVT(), dl, Chain, FIPtr, MachinePointerInfo());
+      FI = MF.getFrameInfo().CreateFixedObject(4, Offset, true);
     } else if (VA.getValVT() == MVT::f128) {
-      report_fatal_error("SPARCv8 does not handle f128 in calls; "
-                         "pass indirectly");
+      FI = MF.getFrameInfo().CreateFixedObject(16, Offset, false);
     } else {
       // We shouldn't see any other value types here.
       llvm_unreachable("Unexpected ValVT encountered in frame lowering.");
     }
+
+    SDValue FIPtr = DAG.getFrameIndex(FI, PtrVT);
+    SDValue Load =
+        DAG.getLoad(VA.getValVT(), dl, Chain, FIPtr, MachinePointerInfo());
     InVals.push_back(Load);
   }
 
@@ -913,7 +912,9 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
     // Promote the value if needed.
     switch (VA.getLocInfo()) {
     default: llvm_unreachable("Unknown loc info!");
-    case CCValAssign::Full: break;
+    case CCValAssign::Full:
+    case CCValAssign::Indirect:
+      break;
     case CCValAssign::SExt:
       Arg = DAG.getNode(ISD::SIGN_EXTEND, dl, VA.getLocVT(), Arg);
       break;

@koachan
Copy link
Contributor Author

koachan commented Oct 7, 2025

Tagging this as WIP since there's some parts that's incomplete, I just want to solicit some advice for now.

The status is that for C code (e.g long double square(long double num) { return num * num; }) it seems to pass and return the values correctly, however with direct LLVM IR like so:

declare fp128 @fmul(fp128 %a, fp128 %b)

define fp128 @square(fp128 %num) {
    %ret = call fp128 @fmul(fp128 %num, fp128 %num)
    ret fp128 %ret
}

I'm still seeing miscompilations:

square:
	save %sp, -120, %sp
	ldd [%fp+92], %f0
	ldd [%fp+100], %f4
	ld [%fp+64], %i0
	add %fp, -16, %i1
	st %i1, [%sp+64]
	std %f4, [%sp+104]
	std %f0, [%sp+96]
	std %f4, [%sp+100]  ! Misaligned store, also overlaps with the one in line 8
	call fmul
	std %f0, [%sp+92]   ! Misaligned store, also overlaps with the one in line 9
	unimp 16
	ldd [%fp+-8], %f0
	ldd [%fp+-16], %f4
	std %f0, [%i0+8]
	std %f4, [%i0]
	ret                 ! Should be jmp [%i7+12]
	restore



// f128 arguments are passed indirectly.
CCIfType<[f128], CCPassIndirect<i32>>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, am I doing this right @efriedma-quic? The argument to CCPassIndirect is the type of the pointer right?

@koachan
Copy link
Contributor Author

koachan commented Oct 7, 2025

declare fp128 @fmul(fp128 %a, fp128 %b)

define fp128 @square(fp128 %num) {
    %ret = call fp128 @fmul(fp128 %num, fp128 %num)
    ret fp128 %ret
}

I think the main issue here is that the fp128s need to be passed around as if it's a sret struct but I dunno how to declare it in (Ret)CC_Sparc32...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:Sparc clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category compiler-rt:builtins compiler-rt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants