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?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks right, I think.

@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...

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

If SparcTargetLowering::CanLowerReturn fails, the value should be automatically converted to sret without any explicit code.

// The SPARCv8 System V ABI has long double 128-bits in size, but 64-bit
// aligned.
LongDoubleWidth = 128;
LongDoubleAlign = 64;
Copy link
Collaborator

Choose a reason for hiding this comment

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

You probably also need to fix the datalayout



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

Choose a reason for hiding this comment

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

Looks right, I think.

} 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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't look right.

If the value is passed indirectly, that's encoded in VA: call getLocInfo(), check for CCValAssign::Indirect. If it's indirect, the argument is a pointer, so you need to load the pointer, then load the underlying value from that pointer.

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.

3 participants