Skip to content

[RISCV] Add support for quadruple-precision floating point ABIs#195166

Open
spaits wants to merge 6 commits intollvm:mainfrom
spaits:llvm/riscv/GISel/ilp32q-lp64q-1
Open

[RISCV] Add support for quadruple-precision floating point ABIs#195166
spaits wants to merge 6 commits intollvm:mainfrom
spaits:llvm/riscv/GISel/ilp32q-lp64q-1

Conversation

@spaits
Copy link
Copy Markdown
Contributor

@spaits spaits commented Apr 30, 2026

Adding support for ilp32q and lp64q in GlobalISel. To have SelDAG tests similar to the existing ABI tests (so basically just add a new RUN line) we would need some more legalization rules for other fp instructions.

@spaits spaits changed the title [RISCV][GISel] Add support for quad precision floating point ABIs [RISCV][GISel] Add support for quadruple-precision floating point ABIs Apr 30, 2026
@spaits spaits marked this pull request as ready for review April 30, 2026 20:17
@llvmorg-github-actions llvmorg-github-actions Bot added backend:RISC-V clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:globalisel labels Apr 30, 2026
@spaits spaits requested review from arsenm, lenary and topperc April 30, 2026 20:17
@llvmorg-github-actions
Copy link
Copy Markdown

@llvm/pr-subscribers-backend-risc-v

Author: Gábor Spaits (spaits)

Changes

Adding support for ilp32q and lp64q in GlobalISel.


Patch is 359.32 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/195166.diff

21 Files Affected:

  • (modified) clang/lib/Basic/Targets/RISCV.cpp (+2)
  • (modified) clang/lib/Basic/Targets/RISCV.h (+4-2)
  • (modified) llvm/lib/CodeGen/GlobalISel/CallLowering.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp (+27-12)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp (+4)
  • (modified) llvm/lib/Target/RISCV/RISCVCallingConv.cpp (+45-4)
  • (modified) llvm/lib/Target/RISCV/RISCVCallingConv.td (+7)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp (+10)
  • (modified) llvm/lib/TargetParser/RISCVISAInfo.cpp (+4)
  • (modified) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll (+1-1187)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-ilp32q-common.ll (+1531)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32d-ilp32q.ll (+555)
  • (removed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32d.ll (-376)
  • (renamed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32f-ilp32d-ilp32q-common.ll (+219-89)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32q.ll (+436)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64d-lp64q.ll (+415)
  • (removed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64d.ll (-283)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64q.ll (+331)
diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp
index 685925b0773dc..a531ee9302442 100644
--- a/clang/lib/Basic/Targets/RISCV.cpp
+++ b/clang/lib/Basic/Targets/RISCV.cpp
@@ -168,6 +168,8 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
     Builder.defineMacro("__riscv_float_abi_single");
   else if (ABIName == "ilp32d" || ABIName == "lp64d")
     Builder.defineMacro("__riscv_float_abi_double");
+  else if (ABIName == "ilp32q" || ABIName == "lp64q")
+    Builder.defineMacro("__riscv_float_abi_quad");
   else
     Builder.defineMacro("__riscv_float_abi_soft");
 
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index 619d491d379d3..c02dbe11c92a4 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -189,7 +189,8 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
       return true;
     }
 
-    if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d") {
+    if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d" ||
+        Name == "ilp32q") {
       ABI = Name;
       return true;
     }
@@ -220,7 +221,8 @@ class LLVM_LIBRARY_VISIBILITY RISCV64TargetInfo : public RISCVTargetInfo {
       return true;
     }
 
-    if (Name == "lp64" || Name == "lp64f" || Name == "lp64d") {
+    if (Name == "lp64" || Name == "lp64f" || Name == "lp64d" ||
+        Name == "lp64q") {
       ABI = Name;
       return true;
     }
diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
index 16549c1047213..b0bf4b684bf6d 100644
--- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
@@ -1347,6 +1347,7 @@ Register CallLowering::ValueHandler::extendRegister(Register ValReg,
     break;
   case CCValAssign::Full:
   case CCValAssign::BCvt:
+  case CCValAssign::Indirect:
     // FIXME: bitconverting between vector types may or may not be a
     // nop in big-endian situations.
     return ValReg;
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
index 60743679f0685..58100331e0f3b 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
@@ -21,6 +21,7 @@
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/Support/ErrorHandling.h"
 
 using namespace llvm;
 
@@ -102,12 +103,19 @@ struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
     assert(VA.getValNo() == VAHi.getValNo() &&
            "Values belong to different arguments");
 
-    assert(VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
-           VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
-           "unexpected custom value");
-
-    Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
-                          MRI.createGenericVirtualRegister(LLT::scalar(32))};
+    uint32_t RegWidth = 0;
+    if (VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
+        VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64)
+      RegWidth = 32;
+    else if (VA.getLocVT() == MVT::i64 && VAHi.getLocVT() == MVT::i64 &&
+             VA.getValVT() == MVT::f128 && VAHi.getValVT() == MVT::f128)
+      RegWidth = 64;
+    else
+      llvm_unreachable("Unexpected custom value");
+
+    Register NewRegs[] = {
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth)),
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth))};
     MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]);
 
     if (VAHi.isMemLoc()) {
@@ -201,12 +209,19 @@ struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
     assert(VA.getValNo() == VAHi.getValNo() &&
            "Values belong to different arguments");
 
-    assert(VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
-           VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
-           "unexpected custom value");
-
-    Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
-                          MRI.createGenericVirtualRegister(LLT::scalar(32))};
+    uint32_t RegWidth = 0;
+    if (VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
+        VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64)
+      RegWidth = 32;
+    else if (VA.getLocVT() == MVT::i64 && VAHi.getLocVT() == MVT::i64 &&
+             VA.getValVT() == MVT::f128 && VAHi.getValVT() == MVT::f128)
+      RegWidth = 64;
+    else
+      llvm_unreachable("Unexpected custom value");
+
+    Register NewRegs[] = {
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth)),
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth))};
 
     if (VAHi.isMemLoc()) {
       LLT MemTy(VAHi.getLocVT());
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
index 62cd6cca49c0e..ac083d98c8d8b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
@@ -106,10 +106,12 @@ ABI getTargetABI(StringRef ABIName) {
                        .Case("ilp32", ABI_ILP32)
                        .Case("ilp32f", ABI_ILP32F)
                        .Case("ilp32d", ABI_ILP32D)
+                       .Case("ilp32q", ABI_ILP32Q)
                        .Case("ilp32e", ABI_ILP32E)
                        .Case("lp64", ABI_LP64)
                        .Case("lp64f", ABI_LP64F)
                        .Case("lp64d", ABI_LP64D)
+                       .Case("lp64q", ABI_LP64Q)
                        .Case("lp64e", ABI_LP64E)
                        .Default(ABI_Unknown);
   return TargetABI;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index b5aade96d4093..1f9c9d16e5bbd 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -666,10 +666,12 @@ enum ABI {
   ABI_ILP32,
   ABI_ILP32F,
   ABI_ILP32D,
+  ABI_ILP32Q,
   ABI_ILP32E,
   ABI_LP64,
   ABI_LP64F,
   ABI_LP64D,
+  ABI_LP64Q,
   ABI_LP64E,
   ABI_Unknown
 };
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 0ca07350d24a0..9f36c0549d9a8 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -133,6 +133,10 @@ void RISCVTargetELFStreamer::finish() {
   case RISCVABI::ABI_LP64D:
     EFlags |= ELF::EF_RISCV_FLOAT_ABI_DOUBLE;
     break;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    EFlags |= ELF::EF_RISCV_FLOAT_ABI_QUAD;
+    break;
   case RISCVABI::ABI_ILP32E:
   case RISCVABI::ABI_LP64E:
     EFlags |= ELF::EF_RISCV_RVE;
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
index 321e986f6f8fd..2c1afa6471615 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
@@ -13,6 +13,7 @@
 #include "RISCVCallingConv.h"
 #include "RISCVMachineFunctionInfo.h"
 #include "RISCVSubtarget.h"
+#include "llvm/CodeGenTypes/MachineValueType.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCRegister.h"
@@ -89,6 +90,9 @@ static const MCPhysReg ArgFPR32s[] = {RISCV::F10_F, RISCV::F11_F, RISCV::F12_F,
 static const MCPhysReg ArgFPR64s[] = {RISCV::F10_D, RISCV::F11_D, RISCV::F12_D,
                                       RISCV::F13_D, RISCV::F14_D, RISCV::F15_D,
                                       RISCV::F16_D, RISCV::F17_D};
+static const MCPhysReg ArgFPR128s[] = {RISCV::F10_Q, RISCV::F11_Q, RISCV::F12_Q,
+                                       RISCV::F13_Q, RISCV::F14_Q, RISCV::F15_Q,
+                                       RISCV::F16_Q, RISCV::F17_Q};
 // This is an interim calling convention and it may be changed in the future.
 static const MCPhysReg ArgVRs[] = {
     RISCV::V8,  RISCV::V9,  RISCV::V10, RISCV::V11, RISCV::V12, RISCV::V13,
@@ -410,6 +414,10 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
   // UseGPRForF64 if targeting soft-float ABIs or an FLEN=32 ABI, if passing a
   // variadic argument, or if no F64 argument registers are available.
   bool UseGPRForF64 = true;
+  // UseGPRForF64 if targeting soft-float ABIs or an FLEN=32 or FLEN=64 ABI, if
+  // passing a variadic argument, or if no F128 argument registers are
+  // available.
+  bool UseGPRForF128 = true;
 
   RISCVABI::ABI ABI = Subtarget.getTargetABI();
   switch (ABI) {
@@ -429,6 +437,12 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     UseGPRForF16_F32 = ArgFlags.isVarArg();
     UseGPRForF64 = ArgFlags.isVarArg();
     break;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    UseGPRForF16_F32 = ArgFlags.isVarArg();
+    UseGPRForF64 = ArgFlags.isVarArg();
+    UseGPRForF128 = ArgFlags.isVarArg();
+    break;
   }
 
   if ((LocVT == MVT::f16 || LocVT == MVT::bf16) && !UseGPRForF16_F32) {
@@ -452,6 +466,13 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     }
   }
 
+  if (LocVT == MVT::f128 && !UseGPRForF128) {
+    if (MCRegister Reg = State.AllocateReg(ArgFPR128s)) {
+      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+      return false;
+    }
+  }
+
   if ((ValVT == MVT::f16 && Subtarget.hasStdExtZhinxmin())) {
     if (MCRegister Reg = State.AllocateReg(getArgGPR16s(ABI))) {
       State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
@@ -524,9 +545,25 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
   assert(PendingLocs.size() == PendingArgFlags.size() &&
          "PendingLocs and PendingArgFlags out of sync");
 
+  // If f128 cannot be passed in an FPR on RV32, it is passed according to the
+  // integer calling convention. Since it is larger than 2*XLEN, pass it by
+  // reference.
+  if (XLen == 32 && LocVT == MVT::f128) {
+    LocVT = XLenVT;
+    LocInfo = CCValAssign::Indirect;
+    if (MCRegister Reg = State.AllocateReg(ArgGPRs))
+      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+    else {
+      int64_t StackOffset = State.AllocateStack(4, Align(4));
+      State.addLoc(
+          CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
+    }
+    return false;
+  }
+
   // Handle passing f64 on RV32D with a soft float ABI or when floating point
   // registers are exhausted.
-  if (XLen == 32 && LocVT == MVT::f64) {
+  if ((XLen == 32 && LocVT == MVT::f64) || (XLen == 64 && LocVT == MVT::f128)) {
     assert(PendingLocs.empty() && "Can't lower f64 if it is split");
     // Depending on available argument GPRS, f64 may be passed in a pair of
     // GPRs, split between a GPR and the stack, or passed completely on the
@@ -534,19 +571,23 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     // cases.
     MCRegister Reg = State.AllocateReg(ArgGPRs);
     if (!Reg) {
-      int64_t StackOffset = State.AllocateStack(8, Align(8));
+      uint32_t AlignForStoredValue = LocVT.getSizeInBits() / 8;
+      int64_t StackOffset =
+          State.AllocateStack(AlignForStoredValue, Align(AlignForStoredValue));
       State.addLoc(
           CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
       return false;
     }
-    LocVT = MVT::i32;
+    LocVT = XLenVT;
     State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
     MCRegister HiReg = State.AllocateReg(ArgGPRs);
     if (HiReg) {
       State.addLoc(
           CCValAssign::getCustomReg(ValNo, ValVT, HiReg, LocVT, LocInfo));
     } else {
-      int64_t StackOffset = State.AllocateStack(4, Align(4));
+      uint32_t AlignForStoredValue = LocVT.getSizeInBits() / 8;
+      int64_t StackOffset =
+          State.AllocateStack(AlignForStoredValue, Align(AlignForStoredValue));
       State.addLoc(
           CCValAssign::getCustomMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
     }
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.td b/llvm/lib/Target/RISCV/RISCVCallingConv.td
index da6b95da12160..843a4ac7e22b9 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.td
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.td
@@ -26,6 +26,10 @@ def CSR_ILP32D_LP64D
     : CalleeSavedRegs<(add CSR_ILP32_LP64,
                        F8_D, F9_D, (sequence "F%u_D", 18, 27))>;
 
+def CSR_ILP32Q_LP64Q
+    : CalleeSavedRegs<(add CSR_ILP32_LP64,
+                       F8_Q, F9_Q, (sequence "F%u_Q", 18, 27))>;
+
 defvar CSR_V = (add (sequence "V%u", 1, 7), (sequence "V%u", 24, 31),
                      V2M2, V4M2, V6M2, V24M2, V26M2, V28M2, V30M2,
                      V4M4, V24M4, V28M4, V24M8);
@@ -39,6 +43,9 @@ def CSR_ILP32F_LP64F_V
 def CSR_ILP32D_LP64D_V
     : CalleeSavedRegs<(add CSR_ILP32D_LP64D, CSR_V)>;
 
+def CSR_ILP32Q_LP64Q_V
+    : CalleeSavedRegs<(add CSR_ILP32Q_LP64Q, CSR_V)>;
+
 // Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
 def CSR_NoRegs : CalleeSavedRegs<(add)>;
 
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index d9dd333519d7c..f6e0c6bdd1425 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -114,6 +114,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
               "doesn't support the D instruction set extension (ignoring "
               "target-abi)\n";
     ABI = Subtarget.is64Bit() ? RISCVABI::ABI_LP64 : RISCVABI::ABI_ILP32;
+  } else if ((ABI == RISCVABI::ABI_ILP32Q || ABI == RISCVABI::ABI_LP64Q) &&
+             !Subtarget.hasStdExtQ()) {
+    errs() << "Hard-float 'q' ABI can't be used for a target that "
+              "doesn't support the Q instruction set extension (ignoring "
+              "target-abi)\n";
+    ABI = Subtarget.is64Bit() ? RISCVABI::ABI_LP64 : RISCVABI::ABI_ILP32;
   }
 
   switch (ABI) {
@@ -124,9 +130,11 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   case RISCVABI::ABI_LP64E:
   case RISCVABI::ABI_ILP32F:
   case RISCVABI::ABI_ILP32D:
+  case RISCVABI::ABI_ILP32Q:
   case RISCVABI::ABI_LP64:
   case RISCVABI::ABI_LP64F:
   case RISCVABI::ABI_LP64D:
+  case RISCVABI::ABI_LP64Q:
     break;
   }
 
@@ -143,6 +151,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     addRegisterClass(MVT::f32, &RISCV::FPR32RegClass);
   if (Subtarget.hasStdExtD())
     addRegisterClass(MVT::f64, &RISCV::FPR64RegClass);
+  if (Subtarget.hasStdExtQ())
+    addRegisterClass(MVT::f128, &RISCV::FPR128RegClass);
   if (Subtarget.hasStdExtZhinxmin())
     addRegisterClass(MVT::f16, &RISCV::GPRF16RegClass);
   if (Subtarget.hasStdExtZfinx())
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 6baa30cf9e6f6..c063f385f94e5 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -117,6 +117,11 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
     if (HasVectorCSR)
       return CSR_ILP32D_LP64D_V_SaveList;
     return CSR_ILP32D_LP64D_SaveList;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    if (HasVectorCSR)
+      return CSR_ILP32Q_LP64Q_V_SaveList;
+    return CSR_ILP32Q_LP64Q_SaveList;
   }
 }
 
@@ -849,6 +854,11 @@ RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF,
     if (CC == CallingConv::RISCV_VectorCall)
       return CSR_ILP32D_LP64D_V_RegMask;
     return CSR_ILP32D_LP64D_RegMask;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    if (CC == CallingConv::RISCV_VectorCall)
+      return CSR_ILP32D_LP64D_V_RegMask;
+    return CSR_ILP32Q_LP64Q_RegMask;
   }
 }
 
diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 973f81d25b321..590c351fa59f7 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -1038,6 +1038,8 @@ StringRef RISCVISAInfo::computeDefaultABI() const {
       return "ilp32d";
     if (Exts.count("f"))
       return "ilp32f";
+    if (Exts.count("q"))
+      return "ilp32q";
     return "ilp32";
   } else if (XLen == 64) {
     if (Exts.count("e"))
@@ -1046,6 +1048,8 @@ StringRef RISCVISAInfo::computeDefaultABI() const {
       return "lp64d";
     if (Exts.count("f"))
       return "lp64f";
+    if (Exts.count("q"))
+      return "lp64q";
     return "lp64";
   }
   llvm_unreachable("Invalid XLEN");
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
index 3225120219c0a..f8b5f08d02c46 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
@@ -13,819 +13,6 @@
 ; ilp32f, and ilp32d ABIs. i.e. where no arguments are passed according to
 ; the floating point ABI.
 
-; Check that on RV32, i64 is passed in a pair of registers. Unlike
-; the convention for varargs, this need not be an aligned pair.
-
-define i32 @callee_i64_in_regs(i32 %a, i64 %b) nounwind {
-  ; RV32I-LABEL: name: callee_i64_in_regs
-  ; RV32I: bb.1 (%ir-block.0):
-  ; RV32I-NEXT:   liveins: $x10, $x11, $x12
-  ; RV32I-NEXT: {{  $}}
-  ; RV32I-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; RV32I-NEXT:   [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
-  ; RV32I-NEXT:   [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
-  ; RV32I-NEXT:   [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
-  ; RV32I-NEXT:   [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[MV]](s64)
-  ; RV32I-NEXT:   [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[TRUNC]]
-  ; RV32I-NEXT:   $x10 = COPY [[ADD]](s32)
-  ; RV32I-NEXT:   PseudoRET implicit $x10
-  %b_trunc = trunc i64 %b to i32
-  %1 = add i32 %a, %b_trunc
-  ret i32 %1
-}
-
-define i32 @caller_i64_in_regs() nounwind {
-  ; ILP32-LABEL: name: caller_i64_in_regs
-  ; ILP32: bb.1 (%ir-block.0):
-  ; ILP32-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ILP32-NEXT:   $x12 = COPY [[UV1]](s32)
-  ; ILP32-NEXT:   PseudoCALL target-flags(riscv-call) @callee_i64_in_regs, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
-  ; ILP32-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; ILP32-NEXT:   $x10 = COPY [[COPY]](s32)
-  ; ILP32-NEXT:   PseudoRET implicit $x10
-  ;
-  ; ILP32F-LABEL: name: caller_i64_in_regs
-  ; ILP32F: bb.1 (%ir-block.0):
-  ; ILP32F-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32F-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32F-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32F-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32F-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32F-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ILP32F-NEXT:   $x12 = COPY [[UV1]](s32)
-  ; ILP32F-NEXT:   PseudoCALL target-flags(riscv-call) @callee_i64_in_regs, csr_ilp32f_lp64f, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
-  ; ILP32F-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32F-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; ILP32F-NEXT:   $x10 = COPY [[COPY]](s32)
-  ; ILP32F-NEXT:   PseudoRET implicit $x10
-  ;
-  ; ILP32D-LABEL: name: caller_i64_in_regs
-  ; ILP32D: bb.1 (%ir-block.0):
-  ; ILP32D-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32D-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32D-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32D-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32D-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32D-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ...
[truncated]

@llvmorg-github-actions
Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-globalisel

Author: Gábor Spaits (spaits)

Changes

Adding support for ilp32q and lp64q in GlobalISel.


Patch is 359.32 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/195166.diff

21 Files Affected:

  • (modified) clang/lib/Basic/Targets/RISCV.cpp (+2)
  • (modified) clang/lib/Basic/Targets/RISCV.h (+4-2)
  • (modified) llvm/lib/CodeGen/GlobalISel/CallLowering.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp (+27-12)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp (+4)
  • (modified) llvm/lib/Target/RISCV/RISCVCallingConv.cpp (+45-4)
  • (modified) llvm/lib/Target/RISCV/RISCVCallingConv.td (+7)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp (+10)
  • (modified) llvm/lib/TargetParser/RISCVISAInfo.cpp (+4)
  • (modified) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll (+1-1187)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-ilp32q-common.ll (+1531)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32d-ilp32q.ll (+555)
  • (removed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32d.ll (-376)
  • (renamed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32f-ilp32d-ilp32q-common.ll (+219-89)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32q.ll (+436)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64d-lp64q.ll (+415)
  • (removed) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64d.ll (-283)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-lp64q.ll (+331)
diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp
index 685925b0773dc..a531ee9302442 100644
--- a/clang/lib/Basic/Targets/RISCV.cpp
+++ b/clang/lib/Basic/Targets/RISCV.cpp
@@ -168,6 +168,8 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
     Builder.defineMacro("__riscv_float_abi_single");
   else if (ABIName == "ilp32d" || ABIName == "lp64d")
     Builder.defineMacro("__riscv_float_abi_double");
+  else if (ABIName == "ilp32q" || ABIName == "lp64q")
+    Builder.defineMacro("__riscv_float_abi_quad");
   else
     Builder.defineMacro("__riscv_float_abi_soft");
 
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index 619d491d379d3..c02dbe11c92a4 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -189,7 +189,8 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
       return true;
     }
 
-    if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d") {
+    if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d" ||
+        Name == "ilp32q") {
       ABI = Name;
       return true;
     }
@@ -220,7 +221,8 @@ class LLVM_LIBRARY_VISIBILITY RISCV64TargetInfo : public RISCVTargetInfo {
       return true;
     }
 
-    if (Name == "lp64" || Name == "lp64f" || Name == "lp64d") {
+    if (Name == "lp64" || Name == "lp64f" || Name == "lp64d" ||
+        Name == "lp64q") {
       ABI = Name;
       return true;
     }
diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
index 16549c1047213..b0bf4b684bf6d 100644
--- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
@@ -1347,6 +1347,7 @@ Register CallLowering::ValueHandler::extendRegister(Register ValReg,
     break;
   case CCValAssign::Full:
   case CCValAssign::BCvt:
+  case CCValAssign::Indirect:
     // FIXME: bitconverting between vector types may or may not be a
     // nop in big-endian situations.
     return ValReg;
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
index 60743679f0685..58100331e0f3b 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
@@ -21,6 +21,7 @@
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/Support/ErrorHandling.h"
 
 using namespace llvm;
 
@@ -102,12 +103,19 @@ struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
     assert(VA.getValNo() == VAHi.getValNo() &&
            "Values belong to different arguments");
 
-    assert(VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
-           VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
-           "unexpected custom value");
-
-    Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
-                          MRI.createGenericVirtualRegister(LLT::scalar(32))};
+    uint32_t RegWidth = 0;
+    if (VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
+        VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64)
+      RegWidth = 32;
+    else if (VA.getLocVT() == MVT::i64 && VAHi.getLocVT() == MVT::i64 &&
+             VA.getValVT() == MVT::f128 && VAHi.getValVT() == MVT::f128)
+      RegWidth = 64;
+    else
+      llvm_unreachable("Unexpected custom value");
+
+    Register NewRegs[] = {
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth)),
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth))};
     MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]);
 
     if (VAHi.isMemLoc()) {
@@ -201,12 +209,19 @@ struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
     assert(VA.getValNo() == VAHi.getValNo() &&
            "Values belong to different arguments");
 
-    assert(VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
-           VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
-           "unexpected custom value");
-
-    Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
-                          MRI.createGenericVirtualRegister(LLT::scalar(32))};
+    uint32_t RegWidth = 0;
+    if (VA.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
+        VA.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64)
+      RegWidth = 32;
+    else if (VA.getLocVT() == MVT::i64 && VAHi.getLocVT() == MVT::i64 &&
+             VA.getValVT() == MVT::f128 && VAHi.getValVT() == MVT::f128)
+      RegWidth = 64;
+    else
+      llvm_unreachable("Unexpected custom value");
+
+    Register NewRegs[] = {
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth)),
+        MRI.createGenericVirtualRegister(LLT::scalar(RegWidth))};
 
     if (VAHi.isMemLoc()) {
       LLT MemTy(VAHi.getLocVT());
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
index 62cd6cca49c0e..ac083d98c8d8b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
@@ -106,10 +106,12 @@ ABI getTargetABI(StringRef ABIName) {
                        .Case("ilp32", ABI_ILP32)
                        .Case("ilp32f", ABI_ILP32F)
                        .Case("ilp32d", ABI_ILP32D)
+                       .Case("ilp32q", ABI_ILP32Q)
                        .Case("ilp32e", ABI_ILP32E)
                        .Case("lp64", ABI_LP64)
                        .Case("lp64f", ABI_LP64F)
                        .Case("lp64d", ABI_LP64D)
+                       .Case("lp64q", ABI_LP64Q)
                        .Case("lp64e", ABI_LP64E)
                        .Default(ABI_Unknown);
   return TargetABI;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index b5aade96d4093..1f9c9d16e5bbd 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -666,10 +666,12 @@ enum ABI {
   ABI_ILP32,
   ABI_ILP32F,
   ABI_ILP32D,
+  ABI_ILP32Q,
   ABI_ILP32E,
   ABI_LP64,
   ABI_LP64F,
   ABI_LP64D,
+  ABI_LP64Q,
   ABI_LP64E,
   ABI_Unknown
 };
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 0ca07350d24a0..9f36c0549d9a8 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -133,6 +133,10 @@ void RISCVTargetELFStreamer::finish() {
   case RISCVABI::ABI_LP64D:
     EFlags |= ELF::EF_RISCV_FLOAT_ABI_DOUBLE;
     break;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    EFlags |= ELF::EF_RISCV_FLOAT_ABI_QUAD;
+    break;
   case RISCVABI::ABI_ILP32E:
   case RISCVABI::ABI_LP64E:
     EFlags |= ELF::EF_RISCV_RVE;
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
index 321e986f6f8fd..2c1afa6471615 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
@@ -13,6 +13,7 @@
 #include "RISCVCallingConv.h"
 #include "RISCVMachineFunctionInfo.h"
 #include "RISCVSubtarget.h"
+#include "llvm/CodeGenTypes/MachineValueType.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCRegister.h"
@@ -89,6 +90,9 @@ static const MCPhysReg ArgFPR32s[] = {RISCV::F10_F, RISCV::F11_F, RISCV::F12_F,
 static const MCPhysReg ArgFPR64s[] = {RISCV::F10_D, RISCV::F11_D, RISCV::F12_D,
                                       RISCV::F13_D, RISCV::F14_D, RISCV::F15_D,
                                       RISCV::F16_D, RISCV::F17_D};
+static const MCPhysReg ArgFPR128s[] = {RISCV::F10_Q, RISCV::F11_Q, RISCV::F12_Q,
+                                       RISCV::F13_Q, RISCV::F14_Q, RISCV::F15_Q,
+                                       RISCV::F16_Q, RISCV::F17_Q};
 // This is an interim calling convention and it may be changed in the future.
 static const MCPhysReg ArgVRs[] = {
     RISCV::V8,  RISCV::V9,  RISCV::V10, RISCV::V11, RISCV::V12, RISCV::V13,
@@ -410,6 +414,10 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
   // UseGPRForF64 if targeting soft-float ABIs or an FLEN=32 ABI, if passing a
   // variadic argument, or if no F64 argument registers are available.
   bool UseGPRForF64 = true;
+  // UseGPRForF64 if targeting soft-float ABIs or an FLEN=32 or FLEN=64 ABI, if
+  // passing a variadic argument, or if no F128 argument registers are
+  // available.
+  bool UseGPRForF128 = true;
 
   RISCVABI::ABI ABI = Subtarget.getTargetABI();
   switch (ABI) {
@@ -429,6 +437,12 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     UseGPRForF16_F32 = ArgFlags.isVarArg();
     UseGPRForF64 = ArgFlags.isVarArg();
     break;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    UseGPRForF16_F32 = ArgFlags.isVarArg();
+    UseGPRForF64 = ArgFlags.isVarArg();
+    UseGPRForF128 = ArgFlags.isVarArg();
+    break;
   }
 
   if ((LocVT == MVT::f16 || LocVT == MVT::bf16) && !UseGPRForF16_F32) {
@@ -452,6 +466,13 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     }
   }
 
+  if (LocVT == MVT::f128 && !UseGPRForF128) {
+    if (MCRegister Reg = State.AllocateReg(ArgFPR128s)) {
+      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+      return false;
+    }
+  }
+
   if ((ValVT == MVT::f16 && Subtarget.hasStdExtZhinxmin())) {
     if (MCRegister Reg = State.AllocateReg(getArgGPR16s(ABI))) {
       State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
@@ -524,9 +545,25 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
   assert(PendingLocs.size() == PendingArgFlags.size() &&
          "PendingLocs and PendingArgFlags out of sync");
 
+  // If f128 cannot be passed in an FPR on RV32, it is passed according to the
+  // integer calling convention. Since it is larger than 2*XLEN, pass it by
+  // reference.
+  if (XLen == 32 && LocVT == MVT::f128) {
+    LocVT = XLenVT;
+    LocInfo = CCValAssign::Indirect;
+    if (MCRegister Reg = State.AllocateReg(ArgGPRs))
+      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+    else {
+      int64_t StackOffset = State.AllocateStack(4, Align(4));
+      State.addLoc(
+          CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
+    }
+    return false;
+  }
+
   // Handle passing f64 on RV32D with a soft float ABI or when floating point
   // registers are exhausted.
-  if (XLen == 32 && LocVT == MVT::f64) {
+  if ((XLen == 32 && LocVT == MVT::f64) || (XLen == 64 && LocVT == MVT::f128)) {
     assert(PendingLocs.empty() && "Can't lower f64 if it is split");
     // Depending on available argument GPRS, f64 may be passed in a pair of
     // GPRs, split between a GPR and the stack, or passed completely on the
@@ -534,19 +571,23 @@ static bool CC_RISCV_Impl(unsigned ValNo, MVT ValVT, MVT LocVT,
     // cases.
     MCRegister Reg = State.AllocateReg(ArgGPRs);
     if (!Reg) {
-      int64_t StackOffset = State.AllocateStack(8, Align(8));
+      uint32_t AlignForStoredValue = LocVT.getSizeInBits() / 8;
+      int64_t StackOffset =
+          State.AllocateStack(AlignForStoredValue, Align(AlignForStoredValue));
       State.addLoc(
           CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
       return false;
     }
-    LocVT = MVT::i32;
+    LocVT = XLenVT;
     State.addLoc(CCValAssign::getCustomReg(ValNo, ValVT, Reg, LocVT, LocInfo));
     MCRegister HiReg = State.AllocateReg(ArgGPRs);
     if (HiReg) {
       State.addLoc(
           CCValAssign::getCustomReg(ValNo, ValVT, HiReg, LocVT, LocInfo));
     } else {
-      int64_t StackOffset = State.AllocateStack(4, Align(4));
+      uint32_t AlignForStoredValue = LocVT.getSizeInBits() / 8;
+      int64_t StackOffset =
+          State.AllocateStack(AlignForStoredValue, Align(AlignForStoredValue));
       State.addLoc(
           CCValAssign::getCustomMem(ValNo, ValVT, StackOffset, LocVT, LocInfo));
     }
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.td b/llvm/lib/Target/RISCV/RISCVCallingConv.td
index da6b95da12160..843a4ac7e22b9 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.td
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.td
@@ -26,6 +26,10 @@ def CSR_ILP32D_LP64D
     : CalleeSavedRegs<(add CSR_ILP32_LP64,
                        F8_D, F9_D, (sequence "F%u_D", 18, 27))>;
 
+def CSR_ILP32Q_LP64Q
+    : CalleeSavedRegs<(add CSR_ILP32_LP64,
+                       F8_Q, F9_Q, (sequence "F%u_Q", 18, 27))>;
+
 defvar CSR_V = (add (sequence "V%u", 1, 7), (sequence "V%u", 24, 31),
                      V2M2, V4M2, V6M2, V24M2, V26M2, V28M2, V30M2,
                      V4M4, V24M4, V28M4, V24M8);
@@ -39,6 +43,9 @@ def CSR_ILP32F_LP64F_V
 def CSR_ILP32D_LP64D_V
     : CalleeSavedRegs<(add CSR_ILP32D_LP64D, CSR_V)>;
 
+def CSR_ILP32Q_LP64Q_V
+    : CalleeSavedRegs<(add CSR_ILP32Q_LP64Q, CSR_V)>;
+
 // Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
 def CSR_NoRegs : CalleeSavedRegs<(add)>;
 
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index d9dd333519d7c..f6e0c6bdd1425 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -114,6 +114,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
               "doesn't support the D instruction set extension (ignoring "
               "target-abi)\n";
     ABI = Subtarget.is64Bit() ? RISCVABI::ABI_LP64 : RISCVABI::ABI_ILP32;
+  } else if ((ABI == RISCVABI::ABI_ILP32Q || ABI == RISCVABI::ABI_LP64Q) &&
+             !Subtarget.hasStdExtQ()) {
+    errs() << "Hard-float 'q' ABI can't be used for a target that "
+              "doesn't support the Q instruction set extension (ignoring "
+              "target-abi)\n";
+    ABI = Subtarget.is64Bit() ? RISCVABI::ABI_LP64 : RISCVABI::ABI_ILP32;
   }
 
   switch (ABI) {
@@ -124,9 +130,11 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   case RISCVABI::ABI_LP64E:
   case RISCVABI::ABI_ILP32F:
   case RISCVABI::ABI_ILP32D:
+  case RISCVABI::ABI_ILP32Q:
   case RISCVABI::ABI_LP64:
   case RISCVABI::ABI_LP64F:
   case RISCVABI::ABI_LP64D:
+  case RISCVABI::ABI_LP64Q:
     break;
   }
 
@@ -143,6 +151,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     addRegisterClass(MVT::f32, &RISCV::FPR32RegClass);
   if (Subtarget.hasStdExtD())
     addRegisterClass(MVT::f64, &RISCV::FPR64RegClass);
+  if (Subtarget.hasStdExtQ())
+    addRegisterClass(MVT::f128, &RISCV::FPR128RegClass);
   if (Subtarget.hasStdExtZhinxmin())
     addRegisterClass(MVT::f16, &RISCV::GPRF16RegClass);
   if (Subtarget.hasStdExtZfinx())
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 6baa30cf9e6f6..c063f385f94e5 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -117,6 +117,11 @@ RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
     if (HasVectorCSR)
       return CSR_ILP32D_LP64D_V_SaveList;
     return CSR_ILP32D_LP64D_SaveList;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    if (HasVectorCSR)
+      return CSR_ILP32Q_LP64Q_V_SaveList;
+    return CSR_ILP32Q_LP64Q_SaveList;
   }
 }
 
@@ -849,6 +854,11 @@ RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF,
     if (CC == CallingConv::RISCV_VectorCall)
       return CSR_ILP32D_LP64D_V_RegMask;
     return CSR_ILP32D_LP64D_RegMask;
+  case RISCVABI::ABI_ILP32Q:
+  case RISCVABI::ABI_LP64Q:
+    if (CC == CallingConv::RISCV_VectorCall)
+      return CSR_ILP32D_LP64D_V_RegMask;
+    return CSR_ILP32Q_LP64Q_RegMask;
   }
 }
 
diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 973f81d25b321..590c351fa59f7 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -1038,6 +1038,8 @@ StringRef RISCVISAInfo::computeDefaultABI() const {
       return "ilp32d";
     if (Exts.count("f"))
       return "ilp32f";
+    if (Exts.count("q"))
+      return "ilp32q";
     return "ilp32";
   } else if (XLen == 64) {
     if (Exts.count("e"))
@@ -1046,6 +1048,8 @@ StringRef RISCVISAInfo::computeDefaultABI() const {
       return "lp64d";
     if (Exts.count("f"))
       return "lp64f";
+    if (Exts.count("q"))
+      return "lp64q";
     return "lp64";
   }
   llvm_unreachable("Invalid XLEN");
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
index 3225120219c0a..f8b5f08d02c46 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/calling-conv-ilp32-ilp32f-ilp32d-common.ll
@@ -13,819 +13,6 @@
 ; ilp32f, and ilp32d ABIs. i.e. where no arguments are passed according to
 ; the floating point ABI.
 
-; Check that on RV32, i64 is passed in a pair of registers. Unlike
-; the convention for varargs, this need not be an aligned pair.
-
-define i32 @callee_i64_in_regs(i32 %a, i64 %b) nounwind {
-  ; RV32I-LABEL: name: callee_i64_in_regs
-  ; RV32I: bb.1 (%ir-block.0):
-  ; RV32I-NEXT:   liveins: $x10, $x11, $x12
-  ; RV32I-NEXT: {{  $}}
-  ; RV32I-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; RV32I-NEXT:   [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
-  ; RV32I-NEXT:   [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
-  ; RV32I-NEXT:   [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
-  ; RV32I-NEXT:   [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[MV]](s64)
-  ; RV32I-NEXT:   [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[TRUNC]]
-  ; RV32I-NEXT:   $x10 = COPY [[ADD]](s32)
-  ; RV32I-NEXT:   PseudoRET implicit $x10
-  %b_trunc = trunc i64 %b to i32
-  %1 = add i32 %a, %b_trunc
-  ret i32 %1
-}
-
-define i32 @caller_i64_in_regs() nounwind {
-  ; ILP32-LABEL: name: caller_i64_in_regs
-  ; ILP32: bb.1 (%ir-block.0):
-  ; ILP32-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ILP32-NEXT:   $x12 = COPY [[UV1]](s32)
-  ; ILP32-NEXT:   PseudoCALL target-flags(riscv-call) @callee_i64_in_regs, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
-  ; ILP32-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; ILP32-NEXT:   $x10 = COPY [[COPY]](s32)
-  ; ILP32-NEXT:   PseudoRET implicit $x10
-  ;
-  ; ILP32F-LABEL: name: caller_i64_in_regs
-  ; ILP32F: bb.1 (%ir-block.0):
-  ; ILP32F-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32F-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32F-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32F-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32F-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32F-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ILP32F-NEXT:   $x12 = COPY [[UV1]](s32)
-  ; ILP32F-NEXT:   PseudoCALL target-flags(riscv-call) @callee_i64_in_regs, csr_ilp32f_lp64f, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
-  ; ILP32F-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32F-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
-  ; ILP32F-NEXT:   $x10 = COPY [[COPY]](s32)
-  ; ILP32F-NEXT:   PseudoRET implicit $x10
-  ;
-  ; ILP32D-LABEL: name: caller_i64_in_regs
-  ; ILP32D: bb.1 (%ir-block.0):
-  ; ILP32D-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
-  ; ILP32D-NEXT:   [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
-  ; ILP32D-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
-  ; ILP32D-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
-  ; ILP32D-NEXT:   $x10 = COPY [[C]](s32)
-  ; ILP32D-NEXT:   $x11 = COPY [[UV]](s32)
-  ; ...
[truncated]

Comment thread llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
@topperc
Copy link
Copy Markdown
Collaborator

topperc commented Apr 30, 2026

What's the end goal here? We don't support Q in SelectionDAG.

@spaits
Copy link
Copy Markdown
Contributor Author

spaits commented Apr 30, 2026

What's the end goal here? We don't support Q in SelectionDAG.

We already have assembly support: spaits@1e503d0 . I was playing around with fp128 related legalizations, and then I saw we already have a RISC-V extension, that supports these types. I saw that the ABI was not implemented for them. It seemed like a fun task to do.

Edit:
Also, i don't know much about SelDAG, but supporting code gen for Q in GISel might not be that hard of a task. I would also like to experiment with that when I will have time for that.

Comment thread llvm/lib/Target/RISCV/RISCVCallingConv.cpp Outdated
Comment thread clang/lib/Basic/Targets/RISCV.h Outdated
@spaits spaits force-pushed the llvm/riscv/GISel/ilp32q-lp64q-1 branch from b0b62e9 to e20bcd3 Compare May 1, 2026 22:17
Copy link
Copy Markdown
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

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

This touches more than just GISel, so remove [GISel] from the description.

I haven't managed a particularly in-depth review, but it's looking mostly right apart from what I commented on below.

I'm not sure we have good handling for dealing with Quad FP values in ISel yet, but I guess that's coming soon.

if (MCRegister Reg = State.AllocateReg(ArgGPRs))
State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
else {
int64_t StackOffset = State.AllocateStack(4, Align(4));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This Allocation is slightly confusing me. Are we allocating for the pointer, or for the f128 value? If the former, use XLenInBytes, otherwise the values seem quite wrong.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We are allocating for the pointer here. In case of indirect parameter passing the actual memory allocation for the passed object is done by the instruction selection framework (For example this is how it is done for GISel: spaits@f450408).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Now using the LocVT variable for the calculation, that is being set to XLenVT in the beginning of the branch (since the new location type is the pointer if we are in that branch. It is done similar in the next branching that is for the "passing the fp128 in two gpr or the stack" case).

@spaits spaits changed the title [RISCV][GISel] Add support for quadruple-precision floating point ABIs [RISCV] Add support for quadruple-precision floating point ABIs May 5, 2026
@@ -143,6 +151,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
addRegisterClass(MVT::f32, &RISCV::FPR32RegClass);
if (Subtarget.hasStdExtD())
addRegisterClass(MVT::f64, &RISCV::FPR64RegClass);
if (Subtarget.hasStdExtQ())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this change make this fail? https://godbolt.org/z/rqb1MY96Y

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have simplified your code to this:

long double foo(long double a) {
  return a + a;
}

Since I don't have a complete RISC-V toolchain at the moment, and the point of the test doesn't seem to be the typedef in the beginning, but rather the foo function.

It fails with SelectionDAG, but work with GlobalISel.

convertLocVTToValVT asserts: Unexpected Custom handling. It is weird for me that we call this function even without quad-precision ABI.

I will check why is this happening.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will probably have to do something similar to this commit spaits@0b4175f . This was the one implementing f64 in two GPRs for rv32.

Copy link
Copy Markdown
Contributor Author

@spaits spaits May 6, 2026

Choose a reason for hiding this comment

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

Or wait, it has worked before. It shouldn't need all this extra code to work after. Maybe I am messing up something in the calling conv? Or maybe, because I have added the line you are referring to will make SDag want to construct an actual hardware fp128 from the GPRs and that is causing the issue? (Sadly SDag is not as easy to work with as GISel (or doesn't seems to be), where I would just dump the irtranslator result and verify what's happening. :( )

@spaits spaits force-pushed the llvm/riscv/GISel/ilp32q-lp64q-1 branch from ce8fe5a to 0b0febe Compare May 6, 2026 10:50
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions cpp,h -- llvm/lib/CodeGen/GlobalISel/CallLowering.cpp llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp llvm/lib/Target/RISCV/RISCVCallingConv.cpp llvm/lib/Target/RISCV/RISCVISelLowering.cpp llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp llvm/lib/TargetParser/RISCVISAInfo.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e7a02efd2..a80bac8b9 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -151,8 +151,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     addRegisterClass(MVT::f32, &RISCV::FPR32RegClass);
   if (Subtarget.hasStdExtD())
     addRegisterClass(MVT::f64, &RISCV::FPR64RegClass);
-  //if (Subtarget.hasStdExtQ())
-  //  addRegisterClass(MVT::f128, &RISCV::FPR128RegClass);
+  // if (Subtarget.hasStdExtQ())
+  //   addRegisterClass(MVT::f128, &RISCV::FPR128RegClass);
   if (Subtarget.hasStdExtZhinxmin())
     addRegisterClass(MVT::f16, &RISCV::GPRF16RegClass);
   if (Subtarget.hasStdExtZfinx())

@spaits
Copy link
Copy Markdown
Contributor Author

spaits commented May 6, 2026

If we disable the addRegisterClass the quad-prec fp registers in ISelLowering, then we will basically have the quad-prec. ABI string, but fp128 will be passed as soft float. That is bad.

If we enable it. It crashes. That's even worse.

To add these ABIs the correct way, tested/stable on not just GISel, but on SelDAG to I will have to add some minimal code gen for Q. To be more precise minimal code gen:

  • so quad-prec. fp + non quad-prec. ABI will work together
  • Addressing the needs of llvm/test/CodeGen/RISCV/calling-conv-* tests. (That is going to be fptosi fp128 codegen).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:globalisel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants