Skip to content

Commit e103515

Browse files
authored
[RISCV][GISel] Support passing arguments through the stack. (#69289)
This is needed when we run out of registers.
1 parent 0913a2d commit e103515

File tree

3 files changed

+607
-9
lines changed

3 files changed

+607
-9
lines changed

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

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "RISCVSubtarget.h"
1818
#include "llvm/CodeGen/Analysis.h"
1919
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
20+
#include "llvm/CodeGen/MachineFrameInfo.h"
2021

2122
using namespace llvm;
2223

@@ -56,19 +57,38 @@ struct RISCVOutgoingValueAssigner : public CallLowering::OutgoingValueAssigner {
5657
struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
5758
RISCVOutgoingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI,
5859
MachineInstrBuilder MIB)
59-
: OutgoingValueHandler(B, MRI), MIB(MIB) {}
60-
61-
MachineInstrBuilder MIB;
62-
60+
: OutgoingValueHandler(B, MRI), MIB(MIB),
61+
Subtarget(MIRBuilder.getMF().getSubtarget<RISCVSubtarget>()) {}
6362
Register getStackAddress(uint64_t MemSize, int64_t Offset,
6463
MachinePointerInfo &MPO,
6564
ISD::ArgFlagsTy Flags) override {
66-
llvm_unreachable("not implemented");
65+
MachineFunction &MF = MIRBuilder.getMF();
66+
LLT p0 = LLT::pointer(0, Subtarget.getXLen());
67+
LLT sXLen = LLT::scalar(Subtarget.getXLen());
68+
69+
if (!SPReg)
70+
SPReg = MIRBuilder.buildCopy(p0, Register(RISCV::X2)).getReg(0);
71+
72+
auto OffsetReg = MIRBuilder.buildConstant(sXLen, Offset);
73+
74+
auto AddrReg = MIRBuilder.buildPtrAdd(p0, SPReg, OffsetReg);
75+
76+
MPO = MachinePointerInfo::getStack(MF, Offset);
77+
return AddrReg.getReg(0);
6778
}
6879

6980
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
7081
MachinePointerInfo &MPO, CCValAssign &VA) override {
71-
llvm_unreachable("not implemented");
82+
MachineFunction &MF = MIRBuilder.getMF();
83+
uint64_t LocMemOffset = VA.getLocMemOffset();
84+
85+
// TODO: Move StackAlignment to subtarget and share with FrameLowering.
86+
auto MMO =
87+
MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, MemTy,
88+
commonAlignment(Align(16), LocMemOffset));
89+
90+
Register ExtReg = extendRegister(ValVReg, VA);
91+
MIRBuilder.buildStore(ExtReg, Addr, *MMO);
7292
}
7393

7494
void assignValueToReg(Register ValVReg, Register PhysReg,
@@ -77,6 +97,14 @@ struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
7797
MIRBuilder.buildCopy(PhysReg, ExtReg);
7898
MIB.addUse(PhysReg, RegState::Implicit);
7999
}
100+
101+
private:
102+
MachineInstrBuilder MIB;
103+
104+
// Cache the SP register vreg if we need it more than once in this call site.
105+
Register SPReg;
106+
107+
const RISCVSubtarget &Subtarget;
80108
};
81109

82110
struct RISCVIncomingValueAssigner : public CallLowering::IncomingValueAssigner {
@@ -112,17 +140,26 @@ struct RISCVIncomingValueAssigner : public CallLowering::IncomingValueAssigner {
112140

113141
struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
114142
RISCVIncomingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI)
115-
: IncomingValueHandler(B, MRI) {}
143+
: IncomingValueHandler(B, MRI),
144+
Subtarget(MIRBuilder.getMF().getSubtarget<RISCVSubtarget>()) {}
116145

117146
Register getStackAddress(uint64_t MemSize, int64_t Offset,
118147
MachinePointerInfo &MPO,
119148
ISD::ArgFlagsTy Flags) override {
120-
llvm_unreachable("not implemented");
149+
MachineFrameInfo &MFI = MIRBuilder.getMF().getFrameInfo();
150+
151+
int FI = MFI.CreateFixedObject(MemSize, Offset, /*Immutable=*/true);
152+
MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI);
153+
return MIRBuilder.buildFrameIndex(LLT::pointer(0, Subtarget.getXLen()), FI)
154+
.getReg(0);
121155
}
122156

123157
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
124158
MachinePointerInfo &MPO, CCValAssign &VA) override {
125-
llvm_unreachable("not implemented");
159+
MachineFunction &MF = MIRBuilder.getMF();
160+
auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOLoad, MemTy,
161+
inferAlignFromPtrInfo(MF, MPO));
162+
MIRBuilder.buildLoad(ValVReg, Addr, *MMO);
126163
}
127164

128165
void assignValueToReg(Register ValVReg, Register PhysReg,
@@ -131,6 +168,9 @@ struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
131168
MIRBuilder.getMBB().addLiveIn(PhysReg);
132169
MIRBuilder.buildCopy(ValVReg, PhysReg);
133170
}
171+
172+
private:
173+
const RISCVSubtarget &Subtarget;
134174
};
135175

136176
struct RISCVCallReturnHandler : public RISCVIncomingValueHandler {
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 3
2+
; RUN: llc -mtriple=riscv32 \
3+
; RUN: -global-isel -stop-after=irtranslator -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+
; RUN: llc -mtriple=riscv32 -mattr=+d -target-abi ilp32d \
9+
; RUN: -global-isel -stop-after=irtranslator -verify-machineinstrs < %s \
10+
; RUN: | FileCheck -check-prefix=RV32I %s
11+
12+
; This file contains tests that should have identical output for the ilp32,
13+
; ilp32f, and ilp32d ABIs. i.e. where no arguments are passed according to
14+
; the floating point ABI.
15+
16+
; Check that on RV32, i64 is passed in a pair of registers. Unlike
17+
; the convention for varargs, this need not be an aligned pair.
18+
19+
define i32 @callee_i64_in_regs(i32 %a, i64 %b) nounwind {
20+
; RV32I-LABEL: name: callee_i64_in_regs
21+
; RV32I: bb.1 (%ir-block.0):
22+
; RV32I-NEXT: liveins: $x10, $x11, $x12
23+
; RV32I-NEXT: {{ $}}
24+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
25+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
26+
; RV32I-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
27+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
28+
; RV32I-NEXT: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[MV]](s64)
29+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[TRUNC]]
30+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
31+
; RV32I-NEXT: PseudoRET implicit $x10
32+
%b_trunc = trunc i64 %b to i32
33+
%1 = add i32 %a, %b_trunc
34+
ret i32 %1
35+
}
36+
37+
define i32 @caller_i64_in_regs() nounwind {
38+
; RV32I-LABEL: name: caller_i64_in_regs
39+
; RV32I: bb.1 (%ir-block.0):
40+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
41+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
42+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C1]](s64)
43+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
44+
; RV32I-NEXT: $x11 = COPY [[UV]](s32)
45+
; RV32I-NEXT: $x12 = COPY [[UV1]](s32)
46+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_i64_in_regs, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit-def $x10
47+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
48+
; RV32I-NEXT: $x10 = COPY [[COPY]](s32)
49+
; RV32I-NEXT: PseudoRET implicit $x10
50+
%1 = call i32 @callee_i64_in_regs(i32 1, i64 2)
51+
ret i32 %1
52+
}
53+
54+
; Check that the stack is used once the GPRs are exhausted
55+
56+
define i32 @callee_many_scalars(i8 %a, i16 %b, i32 %c, i64 %d, i32 %e, i32 %f, i64 %g, i32 %h) nounwind {
57+
; RV32I-LABEL: name: callee_many_scalars
58+
; RV32I: bb.1 (%ir-block.0):
59+
; RV32I-NEXT: liveins: $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17
60+
; RV32I-NEXT: {{ $}}
61+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
62+
; RV32I-NEXT: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[COPY]](s32)
63+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
64+
; RV32I-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC [[COPY1]](s32)
65+
; RV32I-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
66+
; RV32I-NEXT: [[COPY3:%[0-9]+]]:_(s32) = COPY $x13
67+
; RV32I-NEXT: [[COPY4:%[0-9]+]]:_(s32) = COPY $x14
68+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY3]](s32), [[COPY4]](s32)
69+
; RV32I-NEXT: [[COPY5:%[0-9]+]]:_(s32) = COPY $x15
70+
; RV32I-NEXT: [[COPY6:%[0-9]+]]:_(s32) = COPY $x16
71+
; RV32I-NEXT: [[COPY7:%[0-9]+]]:_(s32) = COPY $x17
72+
; RV32I-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %fixed-stack.1
73+
; RV32I-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s32) from %fixed-stack.1, align 16)
74+
; RV32I-NEXT: [[MV1:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY7]](s32), [[LOAD]](s32)
75+
; RV32I-NEXT: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %fixed-stack.0
76+
; RV32I-NEXT: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX1]](p0) :: (load (s32) from %fixed-stack.0)
77+
; RV32I-NEXT: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC]](s8)
78+
; RV32I-NEXT: [[ZEXT1:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC1]](s16)
79+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ZEXT]], [[ZEXT1]]
80+
; RV32I-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[ADD]], [[COPY2]]
81+
; RV32I-NEXT: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[MV]](s64), [[MV1]]
82+
; RV32I-NEXT: [[ZEXT2:%[0-9]+]]:_(s32) = G_ZEXT [[ICMP]](s1)
83+
; RV32I-NEXT: [[ADD2:%[0-9]+]]:_(s32) = G_ADD [[ZEXT2]], [[ADD1]]
84+
; RV32I-NEXT: [[ADD3:%[0-9]+]]:_(s32) = G_ADD [[ADD2]], [[COPY5]]
85+
; RV32I-NEXT: [[ADD4:%[0-9]+]]:_(s32) = G_ADD [[ADD3]], [[COPY6]]
86+
; RV32I-NEXT: [[ADD5:%[0-9]+]]:_(s32) = G_ADD [[ADD4]], [[LOAD1]]
87+
; RV32I-NEXT: $x10 = COPY [[ADD5]](s32)
88+
; RV32I-NEXT: PseudoRET implicit $x10
89+
%a_ext = zext i8 %a to i32
90+
%b_ext = zext i16 %b to i32
91+
%1 = add i32 %a_ext, %b_ext
92+
%2 = add i32 %1, %c
93+
%3 = icmp eq i64 %d, %g
94+
%4 = zext i1 %3 to i32
95+
%5 = add i32 %4, %2
96+
%6 = add i32 %5, %e
97+
%7 = add i32 %6, %f
98+
%8 = add i32 %7, %h
99+
ret i32 %8
100+
}
101+
102+
define i32 @caller_many_scalars() nounwind {
103+
; RV32I-LABEL: name: caller_many_scalars
104+
; RV32I: bb.1 (%ir-block.0):
105+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s8) = G_CONSTANT i8 1
106+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s16) = G_CONSTANT i16 2
107+
; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 3
108+
; RV32I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
109+
; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 5
110+
; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 6
111+
; RV32I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 7
112+
; RV32I-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
113+
; RV32I-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[C]](s8)
114+
; RV32I-NEXT: [[ANYEXT1:%[0-9]+]]:_(s32) = G_ANYEXT [[C1]](s16)
115+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C3]](s64)
116+
; RV32I-NEXT: [[UV2:%[0-9]+]]:_(s32), [[UV3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C6]](s64)
117+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x2
118+
; RV32I-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
119+
; RV32I-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C8]](s32)
120+
; RV32I-NEXT: G_STORE [[UV3]](s32), [[PTR_ADD]](p0) :: (store (s32) into stack, align 16)
121+
; RV32I-NEXT: [[C9:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
122+
; RV32I-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C9]](s32)
123+
; RV32I-NEXT: G_STORE [[C7]](s32), [[PTR_ADD1]](p0) :: (store (s32) into stack + 4)
124+
; RV32I-NEXT: $x10 = COPY [[ANYEXT]](s32)
125+
; RV32I-NEXT: $x11 = COPY [[ANYEXT1]](s32)
126+
; RV32I-NEXT: $x12 = COPY [[C2]](s32)
127+
; RV32I-NEXT: $x13 = COPY [[UV]](s32)
128+
; RV32I-NEXT: $x14 = COPY [[UV1]](s32)
129+
; RV32I-NEXT: $x15 = COPY [[C4]](s32)
130+
; RV32I-NEXT: $x16 = COPY [[C5]](s32)
131+
; RV32I-NEXT: $x17 = COPY [[UV2]](s32)
132+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_many_scalars, implicit-def $x1, implicit $x10, implicit $x11, implicit $x12, implicit $x13, implicit $x14, implicit $x15, implicit $x16, implicit $x17, implicit-def $x10
133+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x10
134+
; RV32I-NEXT: $x10 = COPY [[COPY1]](s32)
135+
; RV32I-NEXT: PseudoRET implicit $x10
136+
%1 = call i32 @callee_many_scalars(i8 1, i16 2, i32 3, i64 4, i32 5, i32 6, i64 7, i32 8)
137+
ret i32 %1
138+
}
139+
140+
; Check return of 2x xlen scalars
141+
142+
define i64 @callee_small_scalar_ret() nounwind {
143+
; RV32I-LABEL: name: callee_small_scalar_ret
144+
; RV32I: bb.1 (%ir-block.0):
145+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1234567898765
146+
; RV32I-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[C]](s64)
147+
; RV32I-NEXT: $x10 = COPY [[UV]](s32)
148+
; RV32I-NEXT: $x11 = COPY [[UV1]](s32)
149+
; RV32I-NEXT: PseudoRET implicit $x10, implicit $x11
150+
ret i64 1234567898765
151+
}
152+
153+
define i32 @caller_small_scalar_ret() nounwind {
154+
; RV32I-LABEL: name: caller_small_scalar_ret
155+
; RV32I: bb.1 (%ir-block.0):
156+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 987654321234567
157+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_small_scalar_ret, implicit-def $x1, implicit-def $x10, implicit-def $x11
158+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
159+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
160+
; RV32I-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
161+
; RV32I-NEXT: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[C]](s64), [[MV]]
162+
; RV32I-NEXT: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[ICMP]](s1)
163+
; RV32I-NEXT: $x10 = COPY [[ZEXT]](s32)
164+
; RV32I-NEXT: PseudoRET implicit $x10
165+
%1 = call i64 @callee_small_scalar_ret()
166+
%2 = icmp eq i64 987654321234567, %1
167+
%3 = zext i1 %2 to i32
168+
ret i32 %3
169+
}
170+
171+
; Check return of 2x xlen structs
172+
173+
%struct.small = type { i32, ptr }
174+
175+
define %struct.small @callee_small_struct_ret() nounwind {
176+
; RV32I-LABEL: name: callee_small_struct_ret
177+
; RV32I: bb.1 (%ir-block.0):
178+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
179+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(p0) = G_CONSTANT i32 0
180+
; RV32I-NEXT: $x10 = COPY [[C]](s32)
181+
; RV32I-NEXT: $x11 = COPY [[C1]](p0)
182+
; RV32I-NEXT: PseudoRET implicit $x10, implicit $x11
183+
ret %struct.small { i32 1, ptr null }
184+
}
185+
186+
define i32 @caller_small_struct_ret() nounwind {
187+
; RV32I-LABEL: name: caller_small_struct_ret
188+
; RV32I: bb.1 (%ir-block.0):
189+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_small_struct_ret, implicit-def $x1, implicit-def $x10, implicit-def $x11
190+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
191+
; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(p0) = COPY $x11
192+
; RV32I-NEXT: [[PTRTOINT:%[0-9]+]]:_(s32) = G_PTRTOINT [[COPY1]](p0)
193+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[PTRTOINT]]
194+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
195+
; RV32I-NEXT: PseudoRET implicit $x10
196+
%1 = call %struct.small @callee_small_struct_ret()
197+
%2 = extractvalue %struct.small %1, 0
198+
%3 = extractvalue %struct.small %1, 1
199+
%4 = ptrtoint ptr %3 to i32
200+
%5 = add i32 %2, %4
201+
ret i32 %5
202+
}
203+
204+
; Check return of >2x xlen structs
205+
206+
%struct.large = type { i32, i32, i32, i32 }
207+
208+
define void @callee_large_struct_ret(ptr noalias sret(%struct.large) %agg.result) nounwind {
209+
; RV32I-LABEL: name: callee_large_struct_ret
210+
; RV32I: bb.1 (%ir-block.0):
211+
; RV32I-NEXT: liveins: $x10
212+
; RV32I-NEXT: {{ $}}
213+
; RV32I-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
214+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
215+
; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
216+
; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 3
217+
; RV32I-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
218+
; RV32I-NEXT: G_STORE [[C]](s32), [[COPY]](p0) :: (store (s32) into %ir.agg.result)
219+
; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
220+
; RV32I-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw G_PTR_ADD [[COPY]], [[C4]](s32)
221+
; RV32I-NEXT: G_STORE [[C1]](s32), [[PTR_ADD]](p0) :: (store (s32) into %ir.b)
222+
; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
223+
; RV32I-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = nuw G_PTR_ADD [[COPY]], [[C5]](s32)
224+
; RV32I-NEXT: G_STORE [[C2]](s32), [[PTR_ADD1]](p0) :: (store (s32) into %ir.c)
225+
; RV32I-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 12
226+
; RV32I-NEXT: [[PTR_ADD2:%[0-9]+]]:_(p0) = nuw G_PTR_ADD [[COPY]], [[C6]](s32)
227+
; RV32I-NEXT: G_STORE [[C3]](s32), [[PTR_ADD2]](p0) :: (store (s32) into %ir.d)
228+
; RV32I-NEXT: PseudoRET
229+
store i32 1, ptr %agg.result, align 4
230+
%b = getelementptr inbounds %struct.large, ptr %agg.result, i32 0, i32 1
231+
store i32 2, ptr %b, align 4
232+
%c = getelementptr inbounds %struct.large, ptr %agg.result, i32 0, i32 2
233+
store i32 3, ptr %c, align 4
234+
%d = getelementptr inbounds %struct.large, ptr %agg.result, i32 0, i32 3
235+
store i32 4, ptr %d, align 4
236+
ret void
237+
}
238+
239+
define i32 @caller_large_struct_ret() nounwind {
240+
; RV32I-LABEL: name: caller_large_struct_ret
241+
; RV32I: bb.1 (%ir-block.0):
242+
; RV32I-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
243+
; RV32I-NEXT: $x10 = COPY [[FRAME_INDEX]](p0)
244+
; RV32I-NEXT: PseudoCALL target-flags(riscv-call) @callee_large_struct_ret, implicit-def $x1, implicit $x10
245+
; RV32I-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (dereferenceable load (s32) from %ir.1)
246+
; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 12
247+
; RV32I-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw G_PTR_ADD [[FRAME_INDEX]], [[C]](s32)
248+
; RV32I-NEXT: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[PTR_ADD]](p0) :: (dereferenceable load (s32) from %ir.3)
249+
; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[LOAD]], [[LOAD1]]
250+
; RV32I-NEXT: $x10 = COPY [[ADD]](s32)
251+
; RV32I-NEXT: PseudoRET implicit $x10
252+
%1 = alloca %struct.large
253+
call void @callee_large_struct_ret(ptr sret(%struct.large) %1)
254+
%2 = load i32, ptr %1
255+
%3 = getelementptr inbounds %struct.large, ptr %1, i32 0, i32 3
256+
%4 = load i32, ptr %3
257+
%5 = add i32 %2, %4
258+
ret i32 %5
259+
}

0 commit comments

Comments
 (0)