llvm.org GIT mirror llvm / 8c575a1
[GlobalISel] Rewrite CallLowering::lowerReturn to accept multiple VRegs per Value This is logical continuation of https://reviews.llvm.org/D46018 (r332449) Differential Revision: https://reviews.llvm.org/D49660 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@338685 91177308-0d34-0410-b5e6-96231b3b80d8 Alexander Ivchenko 2 years ago
16 changed file(s) with 429 addition(s) and 132 deletion(s). Raw diff Collapse all Expand all
137137 virtual ~CallLowering() = default;
138138
139139 /// This hook must be implemented to lower outgoing return values, described
140 /// by \p Val, into the specified virtual register \p VReg.
140 /// by \p Val, into the specified virtual registers \p VRegs.
141141 /// This hook is used by GlobalISel.
142142 ///
143143 /// \return True if the lowering succeeds, false otherwise.
144 virtual bool lowerReturn(MachineIRBuilder &MIRBuilder,
145 const Value *Val, unsigned VReg) const {
144 virtual bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
145 ArrayRef VRegs) const {
146146 return false;
147147 }
148148
322322 const Value *Ret = RI.getReturnValue();
323323 if (Ret && DL->getTypeStoreSize(Ret->getType()) == 0)
324324 Ret = nullptr;
325
326 ArrayRef VRegs;
327 if (Ret)
328 VRegs = getOrCreateVRegs(*Ret);
329
325330 // The target may mess up with the insertion point, but
326331 // this is not important as a return is the last instruction
327332 // of the block anyway.
328333
329 // FIXME: this interface should simplify when CallLowering gets adapted to
330 // multiple VRegs per Value.
331 unsigned VReg = Ret ? packRegs(*Ret, MIRBuilder) : 0;
332 return CLI->lowerReturn(MIRBuilder, Ret, VReg);
334 return CLI->lowerReturn(MIRBuilder, Ret, VRegs);
333335 }
334336
335337 bool IRTranslator::translateBr(const User &U, MachineIRBuilder &MIRBuilder) {
226226 }
227227
228228 bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
229 const Value *Val, unsigned VReg) const {
230 MachineFunction &MF = MIRBuilder.getMF();
231 const Function &F = MF.getFunction();
232
229 const Value *Val,
230 ArrayRef VRegs) const {
233231 auto MIB = MIRBuilder.buildInstrNoInsert(AArch64::RET_ReallyLR);
234 assert(((Val && VReg) || (!Val && !VReg)) && "Return value without a vreg");
232 assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
233 "Return value without a vreg");
234
235235 bool Success = true;
236 if (VReg) {
236 if (!VRegs.empty()) {
237 MachineFunction &MF = MIRBuilder.getMF();
238 const Function &F = MF.getFunction();
239
237240 MachineRegisterInfo &MRI = MF.getRegInfo();
238
239 // We zero-extend i1s to i8.
240 if (MRI.getType(VReg).getSizeInBits() == 1)
241 VReg = MIRBuilder.buildZExt(LLT::scalar(8), VReg)->getOperand(0).getReg();
242
243241 const AArch64TargetLowering &TLI = *getTLI();
244242 CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(F.getCallingConv());
245243 auto &DL = F.getParent()->getDataLayout();
246
247 ArgInfo OrigArg{VReg, Val->getType()};
248 setArgFlags(OrigArg, AttributeList::ReturnIndex, DL, F);
244 LLVMContext &Ctx = Val->getType()->getContext();
245
246 SmallVector SplitEVTs;
247 ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
248 assert(VRegs.size() == SplitEVTs.size() &&
249 "For each split Type there should be exactly one VReg.");
249250
250251 SmallVector SplitArgs;
251 splitToValueTypes(OrigArg, SplitArgs, DL, MRI, F.getCallingConv(),
252 [&](unsigned Reg, uint64_t Offset) {
253 MIRBuilder.buildExtract(Reg, VReg, Offset);
254 });
252 for (unsigned i = 0; i < SplitEVTs.size(); ++i) {
253 // We zero-extend i1s to i8.
254 unsigned CurVReg = VRegs[i];
255 if (MRI.getType(VRegs[i]).getSizeInBits() == 1) {
256 CurVReg = MIRBuilder.buildZExt(LLT::scalar(8), CurVReg)
257 ->getOperand(0)
258 .getReg();
259 }
260
261 ArgInfo CurArgInfo = ArgInfo{CurVReg, SplitEVTs[i].getTypeForEVT(Ctx)};
262 setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
263 splitToValueTypes(CurArgInfo, SplitArgs, DL, MRI, F.getCallingConv(),
264 [&](unsigned Reg, uint64_t Offset) {
265 MIRBuilder.buildExtract(Reg, CurVReg, Offset);
266 });
267 }
255268
256269 OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFn, AssignFn);
257270 Success = handleAssignments(MIRBuilder, SplitArgs, Handler);
3333 public:
3434 AArch64CallLowering(const AArch64TargetLowering &TLI);
3535
36 bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val,
37 unsigned VReg) const override;
36 bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
37 ArrayRef VRegs) const override;
3838
3939 bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
4040 ArrayRef VRegs) const override;
3131 }
3232
3333 bool AMDGPUCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
34 const Value *Val, unsigned VReg) const {
34 const Value *Val,
35 ArrayRef VRegs) const {
3536 // FIXME: Add support for non-void returns.
3637 if (Val)
3738 return false;
3434 public:
3535 AMDGPUCallLowering(const AMDGPUTargetLowering &TLI);
3636
37 bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val,
38 unsigned VReg) const override;
37 bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
38 ArrayRef VRegs) const override;
3939 bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
4040 ArrayRef VRegs) const override;
4141 static CCAssignFn *CCAssignFnForCall(CallingConv::ID CC, bool IsVarArg);
236236 /// Lower the return value for the already existing \p Ret. This assumes that
237237 /// \p MIRBuilder's insertion point is correct.
238238 bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder,
239 const Value *Val, unsigned VReg,
239 const Value *Val, ArrayRef VRegs,
240240 MachineInstrBuilder &Ret) const {
241241 if (!Val)
242242 // Nothing to do here.
250250 if (!isSupportedType(DL, TLI, Val->getType()))
251251 return false;
252252
253 SmallVector SplitEVTs;
254 ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
255 assert(VRegs.size() == SplitEVTs.size() &&
256 "For each split Type there should be exactly one VReg.");
257
253258 SmallVector SplitVTs;
254 SmallVector Regs;
255 ArgInfo RetInfo(VReg, Val->getType());
256 setArgFlags(RetInfo, AttributeList::ReturnIndex, DL, F);
257 splitToValueTypes(RetInfo, SplitVTs, MF, [&](unsigned Reg, uint64_t Offset) {
258 Regs.push_back(Reg);
259 });
260
261 if (Regs.size() > 1)
262 MIRBuilder.buildUnmerge(Regs, VReg);
259 LLVMContext &Ctx = Val->getType()->getContext();
260 for (unsigned i = 0; i < SplitEVTs.size(); ++i) {
261 ArgInfo CurArgInfo(VRegs[i], SplitEVTs[i].getTypeForEVT(Ctx));
262 setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
263
264 SmallVector Regs;
265 splitToValueTypes(
266 CurArgInfo, SplitVTs, MF,
267 [&](unsigned Reg, uint64_t Offset) { Regs.push_back(Reg); });
268 if (Regs.size() > 1)
269 MIRBuilder.buildUnmerge(Regs, VRegs[i]);
270 }
263271
264272 CCAssignFn *AssignFn =
265273 TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg());
269277 }
270278
271279 bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
272 const Value *Val, unsigned VReg) const {
273 assert(!Val == !VReg && "Return value without a vreg");
280 const Value *Val,
281 ArrayRef VRegs) const {
282 assert(!Val == VRegs.empty() && "Return value without a vreg");
274283
275284 auto const &ST = MIRBuilder.getMF().getSubtarget();
276285 unsigned Opcode = ST.getReturnOpcode();
277286 auto Ret = MIRBuilder.buildInstrNoInsert(Opcode).add(predOps(ARMCC::AL));
278287
279 if (!lowerReturnVal(MIRBuilder, Val, VReg, Ret))
288 if (!lowerReturnVal(MIRBuilder, Val, VRegs, Ret))
280289 return false;
281290
282291 MIRBuilder.insertInstr(Ret);
3232 public:
3333 ARMCallLowering(const ARMTargetLowering &TLI);
3434
35 bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val,
36 unsigned VReg) const override;
35 bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
36 ArrayRef VRegs) const override;
3737
3838 bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
3939 ArrayRef VRegs) const override;
4444
4545 private:
4646 bool lowerReturnVal(MachineIRBuilder &MIRBuilder, const Value *Val,
47 unsigned VReg, MachineInstrBuilder &Ret) const;
47 ArrayRef VRegs,
48 MachineInstrBuilder &Ret) const;
4849
4950 using SplitArgTy = std::function;
5051
1515 #include "MipsCallLowering.h"
1616 #include "MipsCCState.h"
1717 #include "MipsTargetMachine.h"
18 #include "llvm/CodeGen/Analysis.h"
1819 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
1920
2021 using namespace llvm;
191192 }
192193
193194 bool MipsCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
194 const Value *Val, unsigned VReg) const {
195 const Value *Val,
196 ArrayRef VRegs) const {
195197
196198 MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(Mips::RetRA);
197199
198 if (Val != nullptr) {
199 if (!isSupportedType(Val->getType()))
200 return false;
201
200 if (Val != nullptr && !isSupportedType(Val->getType()))
201 return false;
202
203 if (!VRegs.empty()) {
202204 MachineFunction &MF = MIRBuilder.getMF();
203205 const Function &F = MF.getFunction();
204206 const DataLayout &DL = MF.getDataLayout();
205207 const MipsTargetLowering &TLI = *getTLI();
208 LLVMContext &Ctx = Val->getType()->getContext();
209
210 SmallVector SplitEVTs;
211 ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
212 assert(VRegs.size() == SplitEVTs.size() &&
213 "For each split Type there should be exactly one VReg.");
206214
207215 SmallVector RetInfos;
208216 SmallVector OrigArgIndices;
209217
210 ArgInfo ArgRetInfo(VReg, Val->getType());
211 setArgFlags(ArgRetInfo, AttributeList::ReturnIndex, DL, F);
212 splitToValueTypes(ArgRetInfo, 0, RetInfos, OrigArgIndices);
218 for (unsigned i = 0; i < SplitEVTs.size(); ++i) {
219 ArgInfo CurArgInfo = ArgInfo{VRegs[i], SplitEVTs[i].getTypeForEVT(Ctx)};
220 setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
221 splitToValueTypes(CurArgInfo, 0, RetInfos, OrigArgIndices);
222 }
213223
214224 SmallVector Outs;
215225 subTargetRegTypeForCallingConv(
4949
5050 MipsCallLowering(const MipsTargetLowering &TLI);
5151
52 bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val,
53 unsigned VReg) const override;
52 bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
53 ArrayRef VRegs) const;
5454
5555 bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
5656 ArrayRef VRegs) const override;
6464 SmallVector Offsets;
6565 ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0);
6666
67 if (SplitVTs.size() != 1) {
68 // TODO: support struct/array split
69 return false;
70 }
67 if (OrigArg.Ty->isVoidTy())
68 return true;
7169
7270 EVT VT = SplitVTs[0];
7371 unsigned NumParts = TLI.getNumRegisters(Context, VT);
184182
185183 } // end anonymous namespace
186184
187 bool X86CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
188 const Value *Val, unsigned VReg) const {
189 assert(((Val && VReg) || (!Val && !VReg)) && "Return value without a vreg");
190
185 bool X86CallLowering::lowerReturn(
186 MachineIRBuilder &MIRBuilder, const Value *Val,
187 ArrayRef VRegs) const {
188 assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
189 "Return value without a vreg");
191190 auto MIB = MIRBuilder.buildInstrNoInsert(X86::RET).addImm(0);
192191
193 if (VReg) {
192 if (!VRegs.empty()) {
194193 MachineFunction &MF = MIRBuilder.getMF();
194 const Function &F = MF.getFunction();
195195 MachineRegisterInfo &MRI = MF.getRegInfo();
196196 auto &DL = MF.getDataLayout();
197 const Function &F = MF.getFunction();
198
199 ArgInfo OrigArg{VReg, Val->getType()};
200 setArgFlags(OrigArg, AttributeList::ReturnIndex, DL, F);
197 LLVMContext &Ctx = Val->getType()->getContext();
198 const X86TargetLowering &TLI = *getTLI();
199
200 SmallVector SplitEVTs;
201 ComputeValueVTs(TLI, DL, Val->getType(), SplitEVTs);
202 assert(VRegs.size() == SplitEVTs.size() &&
203 "For each split Type there should be exactly one VReg.");
201204
202205 SmallVector SplitArgs;
203 if (!splitToValueTypes(OrigArg, SplitArgs, DL, MRI,
204 [&](ArrayRef Regs) {
205 MIRBuilder.buildUnmerge(Regs, VReg);
206 }))
207 return false;
206 for (unsigned i = 0; i < SplitEVTs.size(); ++i) {
207 ArgInfo CurArgInfo = ArgInfo{VRegs[i], SplitEVTs[i].getTypeForEVT(Ctx)};
208 setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F);
209 if (!splitToValueTypes(CurArgInfo, SplitArgs, DL, MRI,
210 [&](ArrayRef Regs) {
211 MIRBuilder.buildUnmerge(Regs, VRegs[i]);
212 }))
213 return false;
214 }
208215
209216 OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, RetCC_X86);
210217 if (!handleAssignments(MIRBuilder, SplitArgs, Handler))
2828 public:
2929 X86CallLowering(const X86TargetLowering &TLI);
3030
31 bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val,
32 unsigned VReg) const override;
31 bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
32 ArrayRef VRegs) const override;
3333
3434 bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
3535 ArrayRef VRegs) const override;
9696 ; CHECK: [[CST2:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
9797 ; CHECK: [[GEP2:%[0-9]+]]:_(p0) = G_GEP [[ADDR]], [[CST2]](s64)
9898 ; CHECK: [[LD3:%[0-9]+]]:_(s32) = G_LOAD [[GEP2]](p0) :: (load 4 from %ir.addr + 16, align 8)
99 ; CHECK: [[IMPDEF:%[0-9]+]]:_(s192) = G_IMPLICIT_DEF
100 ; CHECK: [[INS1:%[0-9]+]]:_(s192) = G_INSERT [[IMPDEF]], [[LD1]](s64), 0
101 ; CHECK: [[INS2:%[0-9]+]]:_(s192) = G_INSERT [[INS1]], [[LD2]](s64), 64
102 ; CHECK: [[VAL:%[0-9]+]]:_(s192) = G_INSERT [[INS2]], [[LD3]](s32), 128
103
104 ; CHECK: [[DBL:%[0-9]+]]:_(s64) = G_EXTRACT [[VAL]](s192), 0
105 ; CHECK: [[I64:%[0-9]+]]:_(s64) = G_EXTRACT [[VAL]](s192), 64
106 ; CHECK: [[I32:%[0-9]+]]:_(s32) = G_EXTRACT [[VAL]](s192), 128
107
108 ; CHECK: $d0 = COPY [[DBL]](s64)
109 ; CHECK: $x0 = COPY [[I64]](s64)
110 ; CHECK: $w1 = COPY [[I32]](s32)
99
100 ; CHECK: $d0 = COPY [[LD1]](s64)
101 ; CHECK: $x0 = COPY [[LD2]](s64)
102 ; CHECK: $w1 = COPY [[LD3]](s32)
111103 ; CHECK: RET_ReallyLR implicit $d0, implicit $x0, implicit $w1
112104 define {double, i64, i32} @test_struct_return({double, i64, i32}* %addr) {
113105 %val = load {double, i64, i32}, {double, i64, i32}* %addr
1818
1919 ; CHECK: [[BAD]].{{[a-z]+}} (landing-pad):
2020 ; CHECK: EH_LABEL
21 ; CHECK: [[PTR:%[0-9]+]]:_(p0) = COPY $x0
21 ; CHECK: [[PTR_RET:%[0-9]+]]:_(p0) = COPY $x0
2222 ; CHECK: [[SEL_PTR:%[0-9]+]]:_(p0) = COPY $x1
23 ; CHECK: [[SEL:%[0-9]+]]:_(s32) = G_PTRTOINT [[SEL_PTR]]
24 ; CHECK: [[UNDEF:%[0-9]+]]:_(s128) = G_IMPLICIT_DEF
25 ; CHECK: [[VAL_WITH_PTR:%[0-9]+]]:_(s128) = G_INSERT [[UNDEF]], [[PTR]](p0), 0
26 ; CHECK: [[PTR_SEL:%[0-9]+]]:_(s128) = G_INSERT [[VAL_WITH_PTR]], [[SEL]](s32), 64
27 ; CHECK: [[PTR_RET:%[0-9]+]]:_(s64) = G_EXTRACT [[PTR_SEL]](s128), 0
28 ; CHECK: [[SEL_RET:%[0-9]+]]:_(s32) = G_EXTRACT [[PTR_SEL]](s128), 64
23 ; CHECK: [[SEL_RET:%[0-9]+]]:_(s32) = G_PTRTOINT [[SEL_PTR]]
2924 ; CHECK: $x0 = COPY [[PTR_RET]]
3025 ; CHECK: $w1 = COPY [[SEL_RET]]
3126
3227 ; CHECK: [[GOOD]].{{[a-z]+}}:
3328 ; CHECK: [[SEL:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
34 ; CHECK: {{%[0-9]+}}:_(s128) = G_INSERT {{%[0-9]+}}, [[SEL]](s32), 64
29 ; CHECK: $w1 = COPY [[SEL]]
3530
3631 define { i8*, i32 } @bar() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
3732 %res32 = invoke i32 @foo(i32 42) to label %continue unwind label %broken
208208 ; CHECK: [[EXT3:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 0
209209 ; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 32
210210 ; CHECK: [[EXT5:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 64
211 ; CHECK: [[IMPDEF2:%[0-9]+]]:_(s96) = G_IMPLICIT_DEF
212 ; CHECK: [[INS3:%[0-9]+]]:_(s96) = G_INSERT [[IMPDEF2]], [[EXT3]](s32), 0
213 ; CHECK: [[INS4:%[0-9]+]]:_(s96) = G_INSERT [[INS3]], [[EXT4]](s32), 32
214 ; CHECK: [[INS5:%[0-9]+]]:_(s96) = G_INSERT [[INS4]], [[EXT5]](s32), 64
215 ; CHECK: [[R0:%[0-9]+]]:_(s32), [[R1:%[0-9]+]]:_(s32), [[R2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INS5]](s96)
216211 ; FIXME: This doesn't seem correct with regard to the AAPCS docs (which say
217212 ; that composite types larger than 4 bytes should be passed through memory),
218213 ; but it's what DAGISel does. We should fix it in the common code for both.
219 ; CHECK: $r0 = COPY [[R0]]
220 ; CHECK: $r1 = COPY [[R1]]
221 ; CHECK: $r2 = COPY [[R2]]
214 ; CHECK: $r0 = COPY [[EXT3]]
215 ; CHECK: $r1 = COPY [[EXT4]]
216 ; CHECK: $r2 = COPY [[EXT5]]
222217 ; CHECK: BX_RET 14, $noreg, implicit $r0, implicit $r1, implicit $r2
223218 entry:
224219 %r = notail call arm_aapcscc [3 x i32] @tiny_int_arrays_target([2 x i32] %arr)
353348 ; CHECK: ADJCALLSTACKUP 8, 0, 14, $noreg, implicit-def $sp, implicit $sp
354349 ; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s64), 0
355350 ; CHECK: [[EXT5:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s64), 32
356 ; CHECK: [[IMPDEF2:%[0-9]+]]:_(s64) = G_IMPLICIT_DEF
357 ; CHECK: [[INS4:%[0-9]+]]:_(s64) = G_INSERT [[IMPDEF2]], [[EXT4]](s32), 0
358 ; CHECK: [[INS5:%[0-9]+]]:_(s64) = G_INSERT [[INS4]], [[EXT5]](s32), 32
359 ; CHECK: [[R0:%[0-9]+]]:_(s32), [[R1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INS5]](s64)
360 ; CHECK: $r0 = COPY [[R0]]
361 ; CHECK: $r1 = COPY [[R1]]
351 ; CHECK: $r0 = COPY [[EXT4]]
352 ; CHECK: $r1 = COPY [[EXT5]]
362353 ; CHECK: BX_RET 14, $noreg, implicit $r0, implicit $r1
363354 entry:
364355 %r = notail call arm_aapcscc [2 x float] @fp_arrays_aapcs_target([3 x double] %arr)
452443 ; CHECK: [[EXT12:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 32
453444 ; CHECK: [[EXT13:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 64
454445 ; CHECK: [[EXT14:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 96
455 ; CHECK: [[IMPDEF4:%[0-9]+]]:_(s128) = G_IMPLICIT_DEF
456 ; CHECK: [[INS11:%[0-9]+]]:_(s128) = G_INSERT [[IMPDEF4]], [[EXT11]](s32), 0
457 ; CHECK: [[INS12:%[0-9]+]]:_(s128) = G_INSERT [[INS11]], [[EXT12]](s32), 32
458 ; CHECK: [[INS13:%[0-9]+]]:_(s128) = G_INSERT [[INS12]], [[EXT13]](s32), 64
459 ; CHECK: [[INS14:%[0-9]+]]:_(s128) = G_INSERT [[INS13]], [[EXT14]](s32), 96
460 ; CHECK: [[R0:%[0-9]+]]:_(s32), [[R1:%[0-9]+]]:_(s32), [[R2:%[0-9]+]]:_(s32), [[R3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INS14]](s128)
461 ; CHECK: $s0 = COPY [[R0]]
462 ; CHECK: $s1 = COPY [[R1]]
463 ; CHECK: $s2 = COPY [[R2]]
464 ; CHECK: $s3 = COPY [[R3]]
446 ; CHECK: $s0 = COPY [[EXT11]]
447 ; CHECK: $s1 = COPY [[EXT12]]
448 ; CHECK: $s2 = COPY [[EXT13]]
449 ; CHECK: $s3 = COPY [[EXT14]]
465450 ; CHECK: BX_RET 14, $noreg, implicit $s0, implicit $s1, implicit $s2, implicit $s3
466451 entry:
467452 %r = notail call arm_aapcs_vfpcc [4 x float] @fp_arrays_aapcs_vfp_target([3 x double] %x, [3 x float] %y, [4 x double] %z)
511496 ; CHECK: ADJCALLSTACKUP 80, 0, 14, $noreg, implicit-def $sp, implicit $sp
512497 ; CHECK: [[EXT1:%[0-9]+]]:_(p0) = G_EXTRACT [[RES_ARR]](s64), 0
513498 ; CHECK: [[EXT2:%[0-9]+]]:_(p0) = G_EXTRACT [[RES_ARR]](s64), 32
514 ; CHECK: [[IMPDEF:%[0-9]+]]:_(s64) = G_IMPLICIT_DEF
515 ; CHECK: [[INS2:%[0-9]+]]:_(s64) = G_INSERT [[IMPDEF]], [[EXT1]](p0), 0
516 ; CHECK: [[INS3:%[0-9]+]]:_(s64) = G_INSERT [[INS2]], [[EXT2]](p0), 32
517 ; CHECK: [[R0:%[0-9]+]]:_(s32), [[R1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INS3]](s64)
518 ; CHECK: $r0 = COPY [[R0]]
519 ; CHECK: $r1 = COPY [[R1]]
499 ; CHECK: $r0 = COPY [[EXT1]]
500 ; CHECK: $r1 = COPY [[EXT2]]
520501 ; CHECK: BX_RET 14, $noreg, implicit $r0, implicit $r1
521502 entry:
522503 %r = notail call arm_aapcscc [2 x i32*] @tough_arrays_target([6 x [4 x i32]] %arr)
547528 ; CHECK: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp
548529 ; CHECK: [[EXT3:%[0-9]+]]:_(s32) = G_EXTRACT [[R]](s64), 0
549530 ; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[R]](s64), 32
550 ; CHECK: [[IMPDEF2:%[0-9]+]]:_(s64) = G_IMPLICIT_DEF
551 ; CHECK: [[INS3:%[0-9]+]]:_(s64) = G_INSERT [[IMPDEF2]], [[EXT3]](s32), 0
552 ; CHECK: [[INS4:%[0-9]+]]:_(s64) = G_INSERT [[INS3]], [[EXT4]](s32), 32
553 ; CHECK: [[R0:%[0-9]+]]:_(s32), [[R1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[INS4]](s64)
554 ; CHECK: $r0 = COPY [[R0]](s32)
555 ; CHECK: $r1 = COPY [[R1]](s32)
531 ; CHECK: $r0 = COPY [[EXT3]](s32)
532 ; CHECK: $r1 = COPY [[EXT4]](s32)
556533 ; CHECK: BX_RET 14, $noreg, implicit $r0, implicit $r1
557534 %r = notail call arm_aapcscc {i32, i32} @structs_target({i32, i32} %x)
558535 ret {i32, i32} %r
0 ; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
1 ; RUN: llc -mtriple=x86_64-linux-gnu -global-isel -stop-after=irtranslator < %s -o - | FileCheck %s --check-prefix=ALL
2
3 %struct.f1 = type { float }
4 %struct.d1 = type { double }
5 %struct.d2 = type { double, double }
6 %struct.i1 = type { i32 }
7 %struct.i2 = type { i32, i32 }
8 %struct.i3 = type { i32, i32, i32 }
9 %struct.i4 = type { i32, i32, i32, i32 }
10
11 define float @test_return_f1(float %f.coerce) {
12 ; ALL-LABEL: name: test_return_f1
13 ; ALL: bb.1.entry:
14 ; ALL: liveins: $xmm0
15 ; ALL: [[COPY:%[0-9]+]]:_(s128) = COPY $xmm0
16 ; ALL: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[COPY]](s128)
17 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
18 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
19 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.f
20 ; ALL: G_STORE [[TRUNC]](s32), [[FRAME_INDEX1]](p0) :: (store 4 into %ir.coerce.dive2)
21 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
22 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
23 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
24 ; ALL: $rdx = COPY [[C]](s64)
25 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
26 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
27 ; ALL: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (load 4 from %ir.coerce.dive13)
28 ; ALL: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXT [[LOAD]](s32)
29 ; ALL: $xmm0 = COPY [[ANYEXT]](s128)
30 ; ALL: RET 0, implicit $xmm0
31 entry:
32 %retval = alloca %struct.f1, align 4
33 %f = alloca %struct.f1, align 4
34 %coerce.dive = getelementptr inbounds %struct.f1, %struct.f1* %f, i32 0, i32 0
35 store float %f.coerce, float* %coerce.dive, align 4
36 %0 = bitcast %struct.f1* %retval to i8*
37 %1 = bitcast %struct.f1* %f to i8*
38 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 %1, i64 4, i1 false)
39 %coerce.dive1 = getelementptr inbounds %struct.f1, %struct.f1* %retval, i32 0, i32 0
40 %2 = load float, float* %coerce.dive1, align 4
41 ret float %2
42 }
43
44 declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) #1
45
46 define double @test_return_d1(double %d.coerce) {
47 ; ALL-LABEL: name: test_return_d1
48 ; ALL: bb.1.entry:
49 ; ALL: liveins: $xmm0
50 ; ALL: [[COPY:%[0-9]+]]:_(s128) = COPY $xmm0
51 ; ALL: [[TRUNC:%[0-9]+]]:_(s64) = G_TRUNC [[COPY]](s128)
52 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
53 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
54 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.d
55 ; ALL: G_STORE [[TRUNC]](s64), [[FRAME_INDEX1]](p0) :: (store 8 into %ir.coerce.dive2)
56 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
57 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
58 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
59 ; ALL: $rdx = COPY [[C]](s64)
60 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
61 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
62 ; ALL: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[FRAME_INDEX]](p0) :: (load 8 from %ir.coerce.dive13)
63 ; ALL: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXT [[LOAD]](s64)
64 ; ALL: $xmm0 = COPY [[ANYEXT]](s128)
65 ; ALL: RET 0, implicit $xmm0
66 entry:
67 %retval = alloca %struct.d1, align 8
68 %d = alloca %struct.d1, align 8
69 %coerce.dive = getelementptr inbounds %struct.d1, %struct.d1* %d, i32 0, i32 0
70 store double %d.coerce, double* %coerce.dive, align 8
71 %0 = bitcast %struct.d1* %retval to i8*
72 %1 = bitcast %struct.d1* %d to i8*
73 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 %1, i64 8, i1 false)
74 %coerce.dive1 = getelementptr inbounds %struct.d1, %struct.d1* %retval, i32 0, i32 0
75 %2 = load double, double* %coerce.dive1, align 8
76 ret double %2
77 }
78
79 define { double, double } @test_return_d2(double %d.coerce0, double %d.coerce1) {
80 ; ALL-LABEL: name: test_return_d2
81 ; ALL: bb.1.entry:
82 ; ALL: liveins: $xmm0, $xmm1
83 ; ALL: [[COPY:%[0-9]+]]:_(s128) = COPY $xmm0
84 ; ALL: [[TRUNC:%[0-9]+]]:_(s64) = G_TRUNC [[COPY]](s128)
85 ; ALL: [[COPY1:%[0-9]+]]:_(s128) = COPY $xmm1
86 ; ALL: [[TRUNC1:%[0-9]+]]:_(s64) = G_TRUNC [[COPY1]](s128)
87 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
88 ; ALL: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
89 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
90 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.d
91 ; ALL: G_STORE [[TRUNC]](s64), [[FRAME_INDEX1]](p0) :: (store 8 into %ir.1)
92 ; ALL: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX1]], [[C]](s64)
93 ; ALL: G_STORE [[TRUNC1]](s64), [[GEP]](p0) :: (store 8 into %ir.2)
94 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
95 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
96 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
97 ; ALL: $rdx = COPY [[C1]](s64)
98 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
99 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
100 ; ALL: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[FRAME_INDEX]](p0) :: (load 8 from %ir.5)
101 ; ALL: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
102 ; ALL: [[GEP1:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX]], [[C2]](s64)
103 ; ALL: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP1]](p0) :: (load 8 from %ir.5 + 8)
104 ; ALL: [[ANYEXT:%[0-9]+]]:_(s128) = G_ANYEXT [[LOAD]](s64)
105 ; ALL: $xmm0 = COPY [[ANYEXT]](s128)
106 ; ALL: [[ANYEXT1:%[0-9]+]]:_(s128) = G_ANYEXT [[LOAD1]](s64)
107 ; ALL: $xmm1 = COPY [[ANYEXT1]](s128)
108 ; ALL: RET 0, implicit $xmm0, implicit $xmm1
109 entry:
110 %retval = alloca %struct.d2, align 8
111 %d = alloca %struct.d2, align 8
112 %0 = bitcast %struct.d2* %d to { double, double }*
113 %1 = getelementptr inbounds { double, double }, { double, double }* %0, i32 0, i32 0
114 store double %d.coerce0, double* %1, align 8
115 %2 = getelementptr inbounds { double, double }, { double, double }* %0, i32 0, i32 1
116 store double %d.coerce1, double* %2, align 8
117 %3 = bitcast %struct.d2* %retval to i8*
118 %4 = bitcast %struct.d2* %d to i8*
119 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %3, i8* align 8 %4, i64 16, i1 false)
120 %5 = bitcast %struct.d2* %retval to { double, double }*
121 %6 = load { double, double }, { double, double }* %5, align 8
122 ret { double, double } %6
123 }
124
125 define i32 @test_return_i1(i32 %i.coerce) {
126 ; ALL-LABEL: name: test_return_i1
127 ; ALL: bb.1.entry:
128 ; ALL: liveins: $edi
129 ; ALL: [[COPY:%[0-9]+]]:_(s32) = COPY $edi
130 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
131 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
132 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.i
133 ; ALL: G_STORE [[COPY]](s32), [[FRAME_INDEX1]](p0) :: (store 4 into %ir.coerce.dive2)
134 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
135 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
136 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
137 ; ALL: $rdx = COPY [[C]](s64)
138 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
139 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
140 ; ALL: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (load 4 from %ir.coerce.dive13)
141 ; ALL: $eax = COPY [[LOAD]](s32)
142 ; ALL: RET 0, implicit $eax
143 entry:
144 %retval = alloca %struct.i1, align 4
145 %i = alloca %struct.i1, align 4
146 %coerce.dive = getelementptr inbounds %struct.i1, %struct.i1* %i, i32 0, i32 0
147 store i32 %i.coerce, i32* %coerce.dive, align 4
148 %0 = bitcast %struct.i1* %retval to i8*
149 %1 = bitcast %struct.i1* %i to i8*
150 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 %1, i64 4, i1 false)
151 %coerce.dive1 = getelementptr inbounds %struct.i1, %struct.i1* %retval, i32 0, i32 0
152 %2 = load i32, i32* %coerce.dive1, align 4
153 ret i32 %2
154 }
155
156 define i64 @test_return_i2(i64 %i.coerce) {
157 ; ALL-LABEL: name: test_return_i2
158 ; ALL: bb.1.entry:
159 ; ALL: liveins: $rdi
160 ; ALL: [[COPY:%[0-9]+]]:_(s64) = COPY $rdi
161 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
162 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
163 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.i
164 ; ALL: G_STORE [[COPY]](s64), [[FRAME_INDEX1]](p0) :: (store 8 into %ir.0, align 4)
165 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
166 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
167 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
168 ; ALL: $rdx = COPY [[C]](s64)
169 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
170 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
171 ; ALL: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[FRAME_INDEX]](p0) :: (load 8 from %ir.3, align 4)
172 ; ALL: $rax = COPY [[LOAD]](s64)
173 ; ALL: RET 0, implicit $rax
174 entry:
175 %retval = alloca %struct.i2, align 4
176 %i = alloca %struct.i2, align 4
177 %0 = bitcast %struct.i2* %i to i64*
178 store i64 %i.coerce, i64* %0, align 4
179 %1 = bitcast %struct.i2* %retval to i8*
180 %2 = bitcast %struct.i2* %i to i8*
181 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 %2, i64 8, i1 false)
182 %3 = bitcast %struct.i2* %retval to i64*
183 %4 = load i64, i64* %3, align 4
184 ret i64 %4
185 }
186
187 define { i64, i32 } @test_return_i3(i64 %i.coerce0, i32 %i.coerce1) {
188 ; ALL-LABEL: name: test_return_i3
189 ; ALL: bb.1.entry:
190 ; ALL: liveins: $esi, $rdi
191 ; ALL: [[COPY:%[0-9]+]]:_(s64) = COPY $rdi
192 ; ALL: [[COPY1:%[0-9]+]]:_(s32) = COPY $esi
193 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
194 ; ALL: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 12
195 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
196 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.i
197 ; ALL: [[FRAME_INDEX2:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.2.coerce
198 ; ALL: [[FRAME_INDEX3:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.3.tmp
199 ; ALL: G_STORE [[COPY]](s64), [[FRAME_INDEX2]](p0) :: (store 8 into %ir.0, align 4)
200 ; ALL: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX2]], [[C]](s64)
201 ; ALL: G_STORE [[COPY1]](s32), [[GEP]](p0) :: (store 4 into %ir.1)
202 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
203 ; ALL: $rdi = COPY [[FRAME_INDEX1]](p0)
204 ; ALL: $rsi = COPY [[FRAME_INDEX2]](p0)
205 ; ALL: $rdx = COPY [[C1]](s64)
206 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
207 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
208 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
209 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
210 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
211 ; ALL: $rdx = COPY [[C1]](s64)
212 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
213 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
214 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
215 ; ALL: $rdi = COPY [[FRAME_INDEX3]](p0)
216 ; ALL: $rsi = COPY [[FRAME_INDEX]](p0)
217 ; ALL: $rdx = COPY [[C1]](s64)
218 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
219 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
220 ; ALL: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[FRAME_INDEX3]](p0) :: (load 8 from %ir.tmp)
221 ; ALL: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
222 ; ALL: [[GEP1:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX3]], [[C2]](s64)
223 ; ALL: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[GEP1]](p0) :: (load 4 from %ir.tmp + 8, align 8)
224 ; ALL: $rax = COPY [[LOAD]](s64)
225 ; ALL: $edx = COPY [[LOAD1]](s32)
226 ; ALL: RET 0, implicit $rax, implicit $edx
227 entry:
228 %retval = alloca %struct.i3, align 4
229 %i = alloca %struct.i3, align 4
230 %coerce = alloca { i64, i32 }, align 4
231 %tmp = alloca { i64, i32 }, align 8
232 %0 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %coerce, i32 0, i32 0
233 store i64 %i.coerce0, i64* %0, align 4
234 %1 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %coerce, i32 0, i32 1
235 store i32 %i.coerce1, i32* %1, align 4
236 %2 = bitcast %struct.i3* %i to i8*
237 %3 = bitcast { i64, i32 }* %coerce to i8*
238 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 %3, i64 12, i1 false)
239 %4 = bitcast %struct.i3* %retval to i8*
240 %5 = bitcast %struct.i3* %i to i8*
241 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %4, i8* align 4 %5, i64 12, i1 false)
242 %6 = bitcast { i64, i32 }* %tmp to i8*
243 %7 = bitcast %struct.i3* %retval to i8*
244 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %6, i8* align 4 %7, i64 12, i1 false)
245 %8 = load { i64, i32 }, { i64, i32 }* %tmp, align 8
246 ret { i64, i32 } %8
247 }
248
249 define { i64, i64 } @test_return_i4(i64 %i.coerce0, i64 %i.coerce1) {
250 ; ALL-LABEL: name: test_return_i4
251 ; ALL: bb.1.entry:
252 ; ALL: liveins: $rdi, $rsi
253 ; ALL: [[COPY:%[0-9]+]]:_(s64) = COPY $rdi
254 ; ALL: [[COPY1:%[0-9]+]]:_(s64) = COPY $rsi
255 ; ALL: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
256 ; ALL: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
257 ; ALL: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.retval
258 ; ALL: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.i
259 ; ALL: G_STORE [[COPY]](s64), [[FRAME_INDEX1]](p0) :: (store 8 into %ir.1, align 4)
260 ; ALL: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX1]], [[C]](s64)
261 ; ALL: G_STORE [[COPY1]](s64), [[GEP]](p0) :: (store 8 into %ir.2, align 4)
262 ; ALL: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
263 ; ALL: $rdi = COPY [[FRAME_INDEX]](p0)
264 ; ALL: $rsi = COPY [[FRAME_INDEX1]](p0)
265 ; ALL: $rdx = COPY [[C1]](s64)
266 ; ALL: CALL64pcrel32 &memcpy, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx
267 ; ALL: ADJCALLSTACKUP64 0, 0, implicit-def $rsp, implicit-def $eflags, implicit-def $ssp, implicit $rsp, implicit $ssp
268 ; ALL: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[FRAME_INDEX]](p0) :: (load 8 from %ir.5, align 4)
269 ; ALL: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
270 ; ALL: [[GEP1:%[0-9]+]]:_(p0) = G_GEP [[FRAME_INDEX]], [[C2]](s64)
271 ; ALL: [[LOAD1:%[0-9]+]]:_(s64) = G_LOAD [[GEP1]](p0) :: (load 8 from %ir.5 + 8, align 4)
272 ; ALL: $rax = COPY [[LOAD]](s64)
273 ; ALL: $rdx = COPY [[LOAD1]](s64)
274 ; ALL: RET 0, implicit $rax, implicit $rdx
275 entry:
276 %retval = alloca %struct.i4, align 4
277 %i = alloca %struct.i4, align 4
278 %0 = bitcast %struct.i4* %i to { i64, i64 }*
279 %1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %0, i32 0, i32 0
280 store i64 %i.coerce0, i64* %1, align 4
281 %2 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %0, i32 0, i32 1
282 store i64 %i.coerce1, i64* %2, align 4
283 %3 = bitcast %struct.i4* %retval to i8*
284 %4 = bitcast %struct.i4* %i to i8*
285 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %3, i8* align 4 %4, i64 16, i1 false)
286 %5 = bitcast %struct.i4* %retval to { i64, i64 }*
287 %6 = load { i64, i64 }, { i64, i64 }* %5, align 4
288 ret { i64, i64 } %6
289 }