[RISCV] Add support for static chain
authormelonedo <funanzeng@gmail.com>
Tue, 16 Aug 2022 04:56:59 +0000 (12:56 +0800)
committermelonedo <funanzeng@gmail.com>
Wed, 9 Nov 2022 08:10:32 +0000 (16:10 +0800)
The static chain parameter is a special parameter that is not passed in the usual argument registers or stack space. For example, in x64 System V ABI it is always passed in R10. Although the ABI of RISCV does not assign a register for this purpose, GCC had support for it on RISC-V a long time ago, and it is exposed via `__builtin_call_with_static_chain` intrinsic, and assign t2 for static chain parameters. This patch also chose t2 for compatibility.

In LLVM, static chain parameters are handled by the `nest` attribute of an argument to a function ([D6332](https://reviews.llvm.org/D6332)), so tests are added to ensure `nest` arguments are handled correctly.

Reviewed By: kito-cheng, MaskRay

Differential Revision: https://reviews.llvm.org/D129106

llvm/lib/Target/RISCV/RISCVISelLowering.cpp
llvm/test/CodeGen/RISCV/ghccc-nest.ll [new file with mode: 0644]
llvm/test/CodeGen/RISCV/nest-register.ll [new file with mode: 0644]

index a7f7732..b084615 100644 (file)
@@ -11078,6 +11078,15 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo,
   assert(XLen == 32 || XLen == 64);
   MVT XLenVT = XLen == 32 ? MVT::i32 : MVT::i64;
 
+  // Static chain parameter must not be passed in normal argument registers,
+  // so we assign t2 for it as done in GCC's __builtin_call_with_static_chain
+  if (ArgFlags.isNest()) {
+    if (unsigned Reg = State.AllocateReg(RISCV::X7)) {
+      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+      return false;
+    }
+  }
+
   // Any return value split in to more than two values can't be returned
   // directly. Vectors are returned via the available vector registers.
   if (!LocVT.isVector() && IsRet && ValNo > 1)
@@ -11618,6 +11627,11 @@ static bool CC_RISCV_GHC(unsigned ValNo, MVT ValVT, MVT LocVT,
                          CCValAssign::LocInfo LocInfo,
                          ISD::ArgFlagsTy ArgFlags, CCState &State) {
 
+  if (ArgFlags.isNest()) {
+    report_fatal_error(
+        "Attribute 'nest' is not supported in GHC calling convention");
+  }
+
   if (LocVT == MVT::i32 || LocVT == MVT::i64) {
     // Pass in STG registers: Base, Sp, Hp, R1, R2, R3, R4, R5, R6, R7, SpLim
     //                        s1    s2  s3  s4  s5  s6  s7  s8  s9  s10 s11
diff --git a/llvm/test/CodeGen/RISCV/ghccc-nest.ll b/llvm/test/CodeGen/RISCV/ghccc-nest.ll
new file mode 100644 (file)
index 0000000..c8ed389
--- /dev/null
@@ -0,0 +1,13 @@
+; RUN: not --crash llc -mtriple=riscv64 -mattr=+f,+d -verify-machineinstrs -filetype=null < %s 2>&1 | FileCheck %s
+; RUN: not --crash llc -mtriple=riscv32 -mattr=+f,+d -verify-machineinstrs -filetype=null < %s 2>&1 | FileCheck %s
+
+define ghccc ptr @nest_receiver(ptr nest %arg) nounwind {
+  ret ptr %arg
+}
+
+define ghccc ptr @nest_caller(ptr %arg) nounwind {
+  %result = call ghccc ptr @nest_receiver(ptr nest %arg)
+  ret ptr %result
+}
+
+; CHECK: LLVM ERROR: Attribute 'nest' is not supported in GHC calling convention
diff --git a/llvm/test/CodeGen/RISCV/nest-register.ll b/llvm/test/CodeGen/RISCV/nest-register.ll
new file mode 100644 (file)
index 0000000..97704eb
--- /dev/null
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV64I %s
+
+; Tests that the 'nest' parameter attribute causes the relevant parameter to be
+; passed in the right register.
+
+define ptr @nest_receiver(ptr nest %arg) nounwind {
+; RV32I-LABEL: nest_receiver:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a0, t2
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: nest_receiver:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    mv a0, t2
+; RV64I-NEXT:    ret
+;
+  ret ptr %arg
+}
+
+define ptr @nest_caller(ptr %arg) nounwind {
+; RV32I-LABEL: nest_caller:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    mv t2, a0
+; RV32I-NEXT:    call nest_receiver@plt
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: nest_caller:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    mv t2, a0
+; RV64I-NEXT:    call nest_receiver@plt
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    ret
+;
+  %result = call ptr @nest_receiver(ptr nest %arg)
+  ret ptr %result
+}