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
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)
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
--- /dev/null
+; 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
--- /dev/null
+; 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
+}