Skip to content

Commit 19d7f6f

Browse files
committed
[RISCV][GISel] Add FP calling convention support using FPR and GPR registers.
This does not include passing values on the stack. Test cases copied from SelectionDAG and converted to check MIR output. Test cases that require stack were removed.
1 parent 674b53d commit 19d7f6f

File tree

8 files changed

+644
-0
lines changed

8 files changed

+644
-0
lines changed

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,46 @@ 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 (Thunk) {
126+
*Thunk = [=]() {
127+
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
128+
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
129+
};
130+
return 1;
131+
}
132+
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
133+
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
134+
return 1;
135+
}
136+
102137
private:
103138
MachineInstrBuilder MIB;
104139

@@ -170,6 +205,32 @@ struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
170205
IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA);
171206
}
172207

208+
unsigned assignCustomValue(CallLowering::ArgInfo &Arg,
209+
ArrayRef<CCValAssign> VAs,
210+
std::function<void()> *Thunk = nullptr) override {
211+
assert(VAs.size() >= 2 && "Expected at least 2 VAs.");
212+
const CCValAssign &VALo = VAs[0];
213+
const CCValAssign &VAHi = VAs[1];
214+
215+
assert(VAHi.needsCustom() && "Value doesn't need custom handling");
216+
assert(VALo.getValNo() == VAHi.getValNo() &&
217+
"Values belong to different arguments");
218+
219+
assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
220+
VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
221+
"unexpected custom value");
222+
223+
Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
224+
MRI.createGenericVirtualRegister(LLT::scalar(32))};
225+
226+
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
227+
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
228+
229+
MIRBuilder.buildMergeLikeInstr(Arg.Regs[0], NewRegs);
230+
231+
return 1;
232+
}
233+
173234
/// How the physical register gets marked varies between formal
174235
/// parameters (it's a basic-block live-in), and a call instruction
175236
/// (it's an implicit-def of the BL).
@@ -212,6 +273,8 @@ static bool isSupportedArgumentType(Type *T, const RISCVSubtarget &Subtarget) {
212273
// supported yet.
213274
if (T->isIntegerTy())
214275
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
276+
if (T->isFloatTy() || T->isDoubleTy())
277+
return true;
215278
if (T->isPointerTy())
216279
return true;
217280
return false;
@@ -223,6 +286,8 @@ static bool isSupportedReturnType(Type *T, const RISCVSubtarget &Subtarget) {
223286
// supported yet.
224287
if (T->isIntegerTy())
225288
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
289+
if (T->isFloatTy() || T->isDoubleTy())
290+
return true;
226291
if (T->isPointerTy())
227292
return true;
228293

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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 float @callee_tiny_scalar_ret() nounwind {
42+
; RV32I-LABEL: name: callee_tiny_scalar_ret
43+
; RV32I: bb.1 (%ir-block.0):
44+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 1.000000e+00
45+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
46+
; RV32I-NEXT: PseudoRET implicit $x10
47+
ret float 1.0
48+
}
49+
50+
define i32 @caller_tiny_scalar_ret() nounwind {
51+
; RV32I-LABEL: name: caller_tiny_scalar_ret
52+
; RV32I: bb.1 (%ir-block.0):
53+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_tiny_scalar_ret, implicit-def $x1, implicit-def $x10
54+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
55+
; RV32I-NEXT: $x10 = COPY [[COPY]](s32)
56+
; RV32I-NEXT: PseudoRET implicit $x10
57+
%1 = call float @callee_tiny_scalar_ret()
58+
%2 = bitcast float %1 to i32
59+
ret i32 %2
60+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
2+
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d \
3+
; RUN: -global-isel -stop-after=irtranslator -verify-machineinstrs < %s \
4+
; RUN: | FileCheck -check-prefix=RV32-ILP32D %s
5+
6+
; This file contains tests that will have differing output for the ilp32/ilp32f
7+
; and ilp32d ABIs.
8+
9+
define i32 @callee_double_in_fpr(i32 %a, double %b) nounwind {
10+
; RV32-ILP32D-LABEL: name: callee_double_in_fpr
11+
; RV32-ILP32D: bb.1 (%ir-block.0):
12+
; RV32-ILP32D-NEXT: liveins: $x10, $f10_d
13+
; RV32-ILP32D-NEXT: {{ $}}
14+
; RV32-ILP32D-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
15+
; RV32-ILP32D-NEXT: [[COPY1:%[0-9]+]]:_(s64) = COPY $f10_d
16+
; RV32-ILP32D-NEXT: [[FPTOSI:%[0-9]+]]:_(s32) = G_FPTOSI [[COPY1]](s64)
17+
; RV32-ILP32D-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[FPTOSI]]
18+
; RV32-ILP32D-NEXT: $x10 = COPY [[ADD]](s32)
19+
; RV32-ILP32D-NEXT: PseudoRET implicit $x10
20+
%b_fptosi = fptosi double %b to i32
21+
%1 = add i32 %a, %b_fptosi
22+
ret i32 %1
23+
}
24+
25+
define i32 @caller_double_in_fpr() nounwind {
26+
; RV32-ILP32D-LABEL: name: caller_double_in_fpr
27+
; RV32-ILP32D: bb.1 (%ir-block.0):
28+
; RV32-ILP32D-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
29+
; RV32-ILP32D-NEXT: [[C1:%[0-9]+]]:_(s64) = G_FCONSTANT double 2.000000e+00
30+
; RV32-ILP32D-NEXT: $x10 = COPY [[C]](s32)
31+
; RV32-ILP32D-NEXT: $f10_d = COPY [[C1]](s64)
32+
; RV32-ILP32D-NEXT: PseudoCALL target-flags(riscv-call) @callee_double_in_fpr, implicit-def $x1, implicit $x10, implicit $f10_d, implicit-def $x10
33+
; RV32-ILP32D-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
34+
; RV32-ILP32D-NEXT: $x10 = COPY [[COPY]](s32)
35+
; RV32-ILP32D-NEXT: PseudoRET implicit $x10
36+
%1 = call i32 @callee_double_in_fpr(i32 1, double 2.0)
37+
ret i32 %1
38+
}
39+
40+
; Must keep define on a single line due to an update_llc_test_checks.py limitation
41+
define i32 @callee_double_in_gpr_exhausted_fprs(double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, double %i) nounwind {
42+
; RV32-ILP32D-LABEL: name: callee_double_in_gpr_exhausted_fprs
43+
; RV32-ILP32D: bb.1 (%ir-block.0):
44+
; RV32-ILP32D-NEXT: liveins: $x10, $x11, $f10_d, $f11_d, $f12_d, $f13_d, $f14_d, $f15_d, $f16_d, $f17_d
45+
; RV32-ILP32D-NEXT: {{ $}}
46+
; RV32-ILP32D-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $f10_d
47+
; RV32-ILP32D-NEXT: [[COPY1:%[0-9]+]]:_(s64) = COPY $f11_d
48+
; RV32-ILP32D-NEXT: [[COPY2:%[0-9]+]]:_(s64) = COPY $f12_d
49+
; RV32-ILP32D-NEXT: [[COPY3:%[0-9]+]]:_(s64) = COPY $f13_d
50+
; RV32-ILP32D-NEXT: [[COPY4:%[0-9]+]]:_(s64) = COPY $f14_d
51+
; RV32-ILP32D-NEXT: [[COPY5:%[0-9]+]]:_(s64) = COPY $f15_d
52+
; RV32-ILP32D-NEXT: [[COPY6:%[0-9]+]]:_(s64) = COPY $f16_d
53+
; RV32-ILP32D-NEXT: [[COPY7:%[0-9]+]]:_(s64) = COPY $f17_d
54+
; RV32-ILP32D-NEXT: [[COPY8:%[0-9]+]]:_(s32) = COPY $x10
55+
; RV32-ILP32D-NEXT: [[COPY9:%[0-9]+]]:_(s32) = COPY $x11
56+
; RV32-ILP32D-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY8]](s32), [[COPY9]](s32)
57+
; RV32-ILP32D-NEXT: [[FPTOSI:%[0-9]+]]:_(s32) = G_FPTOSI [[COPY7]](s64)
58+
; RV32-ILP32D-NEXT: [[FPTOSI1:%[0-9]+]]:_(s32) = G_FPTOSI [[MV]](s64)
59+
; RV32-ILP32D-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[FPTOSI]], [[FPTOSI1]]
60+
; RV32-ILP32D-NEXT: $x10 = COPY [[ADD]](s32)
61+
; RV32-ILP32D-NEXT: PseudoRET implicit $x10
62+
%h_fptosi = fptosi double %h to i32
63+
%i_fptosi = fptosi double %i to i32
64+
%1 = add i32 %h_fptosi, %i_fptosi
65+
ret i32 %1
66+
}
67+
68+
define i32 @caller_double_in_gpr_exhausted_fprs() nounwind {
69+
; RV32-ILP32D-LABEL: name: caller_double_in_gpr_exhausted_fprs
70+
; RV32-ILP32D: bb.1 (%ir-block.0):
71+
; RV32-ILP32D-NEXT: [[C:%[0-9]+]]:_(s64) = G_FCONSTANT double 1.000000e+00
72+
; RV32-ILP32D-NEXT: [[C1:%[0-9]+]]:_(s64) = G_FCONSTANT double 2.000000e+00
73+
; RV32-ILP32D-NEXT: [[C2:%[0-9]+]]:_(s64) = G_FCONSTANT double 3.000000e+00
74+
; RV32-ILP32D-NEXT: [[C3:%[0-9]+]]:_(s64) = G_FCONSTANT double 4.000000e+00
75+
; RV32-ILP32D-NEXT: [[C4:%[0-9]+]]:_(s64) = G_FCONSTANT double 5.000000e+00
76+
; RV32-ILP32D-NEXT: [[C5:%[0-9]+]]:_(s64) = G_FCONSTANT double 6.000000e+00
77+
; RV32-ILP32D-NEXT: [[C6:%[0-9]+]]:_(s64) = G_FCONSTANT double 7.000000e+00
78+
; RV32-ILP32D-NEXT: [[C7:%[0-9]+]]:_(s64) = G_FCONSTANT double 8.000000e+00
79+
; RV32-ILP32D-NEXT: [[C8:%[0-9]+]]:_(s64) = G_FCONSTANT double 9.000000e+00
80+
; RV32-ILP32D-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C8]](s64)
81+
; RV32-ILP32D-NEXT: $f10_d = COPY [[C]](s64)
82+
; RV32-ILP32D-NEXT: $f11_d = COPY [[C1]](s64)
83+
; RV32-ILP32D-NEXT: $f12_d = COPY [[C2]](s64)
84+
; RV32-ILP32D-NEXT: $f13_d = COPY [[C3]](s64)
85+
; RV32-ILP32D-NEXT: $f14_d = COPY [[C4]](s64)
86+
; RV32-ILP32D-NEXT: $f15_d = COPY [[C5]](s64)
87+
; RV32-ILP32D-NEXT: $f16_d = COPY [[C6]](s64)
88+
; RV32-ILP32D-NEXT: $f17_d = COPY [[C7]](s64)
89+
; RV32-ILP32D-NEXT: $x10 = COPY [[UV]](s32)
90+
; RV32-ILP32D-NEXT: $x11 = COPY [[UV1]](s32)
91+
; RV32-ILP32D-NEXT: PseudoCALL target-flags(riscv-call) @callee_double_in_gpr_exhausted_fprs, implicit-def $x1, implicit $f10_d, implicit $f11_d, implicit $f12_d, implicit $f13_d, implicit $f14_d, implicit $f15_d, implicit $f16_d, implicit $f17_d, implicit $x10, implicit $x11, implicit-def $x10
92+
; RV32-ILP32D-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
93+
; RV32-ILP32D-NEXT: $x10 = COPY [[COPY]](s32)
94+
; RV32-ILP32D-NEXT: PseudoRET implicit $x10
95+
%1 = call i32 @callee_double_in_gpr_exhausted_fprs(
96+
double 1.0, double 2.0, double 3.0, double 4.0, double 5.0, double 6.0,
97+
double 7.0, double 8.0, double 9.0)
98+
ret i32 %1
99+
}
100+
101+
define double @callee_double_ret() nounwind {
102+
; RV32-ILP32D-LABEL: name: callee_double_ret
103+
; RV32-ILP32D: bb.1 (%ir-block.0):
104+
; RV32-ILP32D-NEXT: [[C:%[0-9]+]]:_(s64) = G_FCONSTANT double 1.000000e+00
105+
; RV32-ILP32D-NEXT: $f10_d = COPY [[C]](s64)
106+
; RV32-ILP32D-NEXT: PseudoRET implicit $f10_d
107+
ret double 1.0
108+
}
109+
110+
define i32 @caller_double_ret() nounwind {
111+
; RV32-ILP32D-LABEL: name: caller_double_ret
112+
; RV32-ILP32D: bb.1 (%ir-block.0):
113+
; RV32-ILP32D-NEXT: PseudoCALL target-flags(riscv-call) @callee_double_ret, implicit-def $x1, implicit-def $f10_d
114+
; RV32-ILP32D-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $f10_d
115+
; RV32-ILP32D-NEXT: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[COPY]](s64)
116+
; RV32-ILP32D-NEXT: $x10 = COPY [[TRUNC]](s32)
117+
; RV32-ILP32D-NEXT: PseudoRET implicit $x10
118+
%1 = call double @callee_double_ret()
119+
%2 = bitcast double %1 to i64
120+
%3 = trunc i64 %2 to i32
121+
ret i32 %3
122+
}

0 commit comments

Comments
 (0)