Skip to content

Commit d32e801

Browse files
authored
[RISCV][GISel] Add FP calling convention support (#69138)
This includes support for using GPRs, FPRs, and stack.
1 parent 17b5445 commit d32e801

8 files changed

+1341
-0
lines changed

llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,60 @@ struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
9494

9595
void assignValueToReg(Register ValVReg, Register PhysReg,
9696
const CCValAssign &VA) override {
97+
// If we're passing an f32 value into an i64, anyextend before copying.
98+
if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32)
99+
ValVReg = MIRBuilder.buildAnyExt(LLT::scalar(64), ValVReg).getReg(0);
100+
97101
Register ExtReg = extendRegister(ValVReg, VA);
98102
MIRBuilder.buildCopy(PhysReg, ExtReg);
99103
MIB.addUse(PhysReg, RegState::Implicit);
100104
}
101105

106+
unsigned assignCustomValue(CallLowering::ArgInfo &Arg,
107+
ArrayRef<CCValAssign> VAs,
108+
std::function<void()> *Thunk) override {
109+
assert(VAs.size() >= 2 && "Expected at least 2 VAs.");
110+
const CCValAssign &VALo = VAs[0];
111+
const CCValAssign &VAHi = VAs[1];
112+
113+
assert(VAHi.needsCustom() && "Value doesn't need custom handling");
114+
assert(VALo.getValNo() == VAHi.getValNo() &&
115+
"Values belong to different arguments");
116+
117+
assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
118+
VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
119+
"unexpected custom value");
120+
121+
Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
122+
MRI.createGenericVirtualRegister(LLT::scalar(32))};
123+
MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]);
124+
125+
if (VAHi.isMemLoc()) {
126+
LLT MemTy(VAHi.getLocVT());
127+
128+
MachinePointerInfo MPO;
129+
Register StackAddr = getStackAddress(
130+
MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]);
131+
132+
assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO,
133+
const_cast<CCValAssign &>(VAHi));
134+
}
135+
136+
auto assignFunc = [=]() {
137+
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
138+
if (VAHi.isRegLoc())
139+
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
140+
};
141+
142+
if (Thunk) {
143+
*Thunk = assignFunc;
144+
return 1;
145+
}
146+
147+
assignFunc();
148+
return 1;
149+
}
150+
102151
private:
103152
MachineInstrBuilder MIB;
104153

@@ -170,6 +219,44 @@ struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
170219
IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA);
171220
}
172221

222+
unsigned assignCustomValue(CallLowering::ArgInfo &Arg,
223+
ArrayRef<CCValAssign> VAs,
224+
std::function<void()> *Thunk) override {
225+
assert(VAs.size() >= 2 && "Expected at least 2 VAs.");
226+
const CCValAssign &VALo = VAs[0];
227+
const CCValAssign &VAHi = VAs[1];
228+
229+
assert(VAHi.needsCustom() && "Value doesn't need custom handling");
230+
assert(VALo.getValNo() == VAHi.getValNo() &&
231+
"Values belong to different arguments");
232+
233+
assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
234+
VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
235+
"unexpected custom value");
236+
237+
Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
238+
MRI.createGenericVirtualRegister(LLT::scalar(32))};
239+
240+
if (VAHi.isMemLoc()) {
241+
LLT MemTy(VAHi.getLocVT());
242+
243+
MachinePointerInfo MPO;
244+
Register StackAddr = getStackAddress(
245+
MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]);
246+
247+
assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO,
248+
const_cast<CCValAssign &>(VAHi));
249+
}
250+
251+
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
252+
if (VAHi.isRegLoc())
253+
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
254+
255+
MIRBuilder.buildMergeLikeInstr(Arg.Regs[0], NewRegs);
256+
257+
return 1;
258+
}
259+
173260
/// How the physical register gets marked varies between formal
174261
/// parameters (it's a basic-block live-in), and a call instruction
175262
/// (it's an implicit-def of the BL).
@@ -212,6 +299,8 @@ static bool isSupportedArgumentType(Type *T, const RISCVSubtarget &Subtarget) {
212299
// supported yet.
213300
if (T->isIntegerTy())
214301
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
302+
if (T->isFloatTy() || T->isDoubleTy())
303+
return true;
215304
if (T->isPointerTy())
216305
return true;
217306
return false;
@@ -223,6 +312,8 @@ static bool isSupportedReturnType(Type *T, const RISCVSubtarget &Subtarget) {
223312
// supported yet.
224313
if (T->isIntegerTy())
225314
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
315+
if (T->isFloatTy() || T->isDoubleTy())
316+
return true;
226317
if (T->isPointerTy())
227318
return true;
228319

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
2+
; RUN: llc -mtriple=riscv32 -global-isel -stop-after=irtranslator \
3+
; RUN: -verify-machineinstrs < %s \
4+
; RUN: | FileCheck -check-prefix=RV32I %s
5+
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi ilp32f \
6+
; RUN: -global-isel -stop-after=irtranslator -verify-machineinstrs < %s \
7+
; RUN: | FileCheck -check-prefix=RV32I %s
8+
9+
; This file contains tests that should have identical output for the ilp32,
10+
; and ilp32f.
11+
12+
; Check that on RV32 ilp32[f], double is passed in a pair of registers. Unlike
13+
; the convention for varargs, this need not be an aligned pair.
14+
15+
define i32 @callee_double_in_regs(i32 %a, double %b) nounwind {
16+
; RV32I-LABEL: name: callee_double_in_regs
17+
; RV32I: bb.1 (%ir-block.0):
18+
; RV32I-NEXT: liveins: $x10, $x11, $x12
19+
; RV32I-NEXT: {{ $}}
20+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
21+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
22+
; RV32I-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
23+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
24+
; RV32I-NEXT: [[FPTOSI:%[0-9]+]]:_(s32) = G_FPTOSI [[MV]](s64)
25+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[FPTOSI]]
26+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
27+
; RV32I-NEXT: PseudoRET implicit $x10
28+
%b_fptosi = fptosi double %b to i32
29+
%1 = add i32 %a, %b_fptosi
30+
ret i32 %1
31+
}
32+
33+
define i32 @caller_double_in_regs() nounwind {
34+
; RV32I-LABEL: name: caller_double_in_regs
35+
; RV32I: bb.1 (%ir-block.0):
36+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
37+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_FCONSTANT double 2.000000e+00
38+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
39+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
40+
; RV32I-NEXT: $x11 = COPY [[UV]](s32)
41+
; RV32I-NEXT: $x12 = COPY [[UV1]](s32)
42+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_double_in_regs, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
43+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
44+
; RV32I-NEXT: $x10 = COPY [[COPY]](s32)
45+
; RV32I-NEXT: PseudoRET implicit $x10
46+
%1 = call i32 @callee_double_in_regs(i32 1, double 2.0)
47+
ret i32 %1
48+
}
49+
50+
define double @callee_small_scalar_ret() nounwind {
51+
; RV32I-LABEL: name: callee_small_scalar_ret
52+
; RV32I: bb.1 (%ir-block.0):
53+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s64) = G_FCONSTANT double 1.000000e+00
54+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C]](s64)
55+
; RV32I-NEXT: $x10 = COPY [[UV]](s32)
56+
; RV32I-NEXT: $x11 = COPY [[UV1]](s32)
57+
; RV32I-NEXT: PseudoRET implicit $x10, implicit $x11
58+
ret double 1.0
59+
}
60+
61+
define i64 @caller_small_scalar_ret() nounwind {
62+
; RV32I-LABEL: name: caller_small_scalar_ret
63+
; RV32I: bb.1 (%ir-block.0):
64+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_small_scalar_ret, implicit-def $x1, implicit-def $x10, implicit-def $x11
65+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
66+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
67+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
68+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[MV]](s64)
69+
; RV32I-NEXT: $x10 = COPY [[UV]](s32)
70+
; RV32I-NEXT: $x11 = COPY [[UV1]](s32)
71+
; RV32I-NEXT: PseudoRET implicit $x10, implicit $x11
72+
%1 = call double @callee_small_scalar_ret()
73+
%2 = bitcast double %1 to i64
74+
ret i64 %2
75+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
2+
; RUN: llc -mtriple=riscv32 -global-isel -stop-after=irtranslator \
3+
; RUN: -verify-machineinstrs < %s | FileCheck -check-prefix=RV32I %s
4+
5+
; Any tests that would have identical output for some combination of the ilp32*
6+
; ABIs belong in calling-conv-*-common.ll. This file contains tests that will
7+
; have different output across those ABIs. i.e. where some arguments would be
8+
; passed according to the floating point ABI.
9+
10+
define i32 @callee_float_in_regs(i32 %a, float %b) nounwind {
11+
; RV32I-LABEL: name: callee_float_in_regs
12+
; RV32I: bb.1 (%ir-block.0):
13+
; RV32I-NEXT: liveins: $x10, $x11
14+
; RV32I-NEXT: {{ $}}
15+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
16+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
17+
; RV32I-NEXT: [[FPTOSI:%[0-9]+]]:_(s32) = G_FPTOSI [[COPY1]](s32)
18+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[FPTOSI]]
19+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
20+
; RV32I-NEXT: PseudoRET implicit $x10
21+
%b_fptosi = fptosi float %b to i32
22+
%1 = add i32 %a, %b_fptosi
23+
ret i32 %1
24+
}
25+
26+
define i32 @caller_float_in_regs() nounwind {
27+
; RV32I-LABEL: name: caller_float_in_regs
28+
; RV32I: bb.1 (%ir-block.0):
29+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
30+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_FCONSTANT float 2.000000e+00
31+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
32+
; RV32I-NEXT: $x11 = COPY [[C1]](s32)
33+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_float_in_regs, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
34+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
35+
; RV32I-NEXT: $x10 = COPY [[COPY]](s32)
36+
; RV32I-NEXT: PseudoRET implicit $x10
37+
%1 = call i32 @callee_float_in_regs(i32 1, float 2.0)
38+
ret i32 %1
39+
}
40+
41+
define i32 @callee_float_on_stack(i64 %a, i64 %b, i64 %c, i64 %d, float %e) nounwind {
42+
; RV32I-LABEL: name: callee_float_on_stack
43+
; RV32I: bb.1 (%ir-block.0):
44+
; RV32I-NEXT: liveins: $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17
45+
; RV32I-NEXT: {{ $}}
46+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
47+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
48+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
49+
; RV32I-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
50+
; RV32I-NEXT: [[COPY3:%[0-9]+]]:_(s32) = COPY $x13
51+
; RV32I-NEXT: [[MV1:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY2]](s32), [[COPY3]](s32)
52+
; RV32I-NEXT: [[COPY4:%[0-9]+]]:_(s32) = COPY $x14
53+
; RV32I-NEXT: [[COPY5:%[0-9]+]]:_(s32) = COPY $x15
54+
; RV32I-NEXT: [[MV2:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY4]](s32), [[COPY5]](s32)
55+
; RV32I-NEXT: [[COPY6:%[0-9]+]]:_(s32) = COPY $x16
56+
; RV32I-NEXT: [[COPY7:%[0-9]+]]:_(s32) = COPY $x17
57+
; RV32I-NEXT: [[MV3:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY6]](s32), [[COPY7]](s32)
58+
; RV32I-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %fixed-stack.0
59+
; RV32I-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s32) from %fixed-stack.0, align 16)
60+
; RV32I-NEXT: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[MV3]](s64)
61+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[TRUNC]], [[LOAD]]
62+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
63+
; RV32I-NEXT: PseudoRET implicit $x10
64+
%1 = trunc i64 %d to i32
65+
%2 = bitcast float %e to i32
66+
%3 = add i32 %1, %2
67+
ret i32 %3
68+
}
69+
70+
define i32 @caller_float_on_stack() nounwind {
71+
; RV32I-LABEL: name: caller_float_on_stack
72+
; RV32I: bb.1 (%ir-block.0):
73+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
74+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
75+
; RV32I-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 3
76+
; RV32I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
77+
; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_FCONSTANT float 5.000000e+00
78+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C]](s64)
79+
; RV32I-NEXT: [[UV2:%[0-9]+]]:_(s32), [[UV3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
80+
; RV32I-NEXT: [[UV4:%[0-9]+]]:_(s32), [[UV5:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C2]](s64)
81+
; RV32I-NEXT: [[UV6:%[0-9]+]]:_(s32), [[UV7:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C3]](s64)
82+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x2
83+
; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
84+
; RV32I-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C5]](s32)
85+
; RV32I-NEXT: G_STORE [[C4]](s32), [[PTR_ADD]](p0) :: (store (s32) into stack, align 16)
86+
; RV32I-NEXT: $x10 = COPY [[UV]](s32)
87+
; RV32I-NEXT: $x11 = COPY [[UV1]](s32)
88+
; RV32I-NEXT: $x12 = COPY [[UV2]](s32)
89+
; RV32I-NEXT: $x13 = COPY [[UV3]](s32)
90+
; RV32I-NEXT: $x14 = COPY [[UV4]](s32)
91+
; RV32I-NEXT: $x15 = COPY [[UV5]](s32)
92+
; RV32I-NEXT: $x16 = COPY [[UV6]](s32)
93+
; RV32I-NEXT: $x17 = COPY [[UV7]](s32)
94+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_float_on_stack, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit $x13, implicit $x14, implicit $x15, implicit $x16, implicit $x17, implicit-def $x10
95+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x10
96+
; RV32I-NEXT: $x10 = COPY [[COPY1]](s32)
97+
; RV32I-NEXT: PseudoRET implicit $x10
98+
%1 = call i32 @callee_float_on_stack(i64 1, i64 2, i64 3, i64 4, float 5.0)
99+
ret i32 %1
100+
}
101+
102+
define float @callee_tiny_scalar_ret() nounwind {
103+
; RV32I-LABEL: name: callee_tiny_scalar_ret
104+
; RV32I: bb.1 (%ir-block.0):
105+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 1.000000e+00
106+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
107+
; RV32I-NEXT: PseudoRET implicit $x10
108+
ret float 1.0
109+
}
110+
111+
define i32 @caller_tiny_scalar_ret() nounwind {
112+
; RV32I-LABEL: name: caller_tiny_scalar_ret
113+
; RV32I: bb.1 (%ir-block.0):
114+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_tiny_scalar_ret, implicit-def $x1, implicit-def $x10
115+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
116+
; RV32I-NEXT: $x10 = COPY [[COPY]](s32)
117+
; RV32I-NEXT: PseudoRET implicit $x10
118+
%1 = call float @callee_tiny_scalar_ret()
119+
%2 = bitcast float %1 to i32
120+
ret i32 %2
121+
}

0 commit comments

Comments
 (0)