[GlobalISel] Accept multiple vregs for lowerCall's result

Change the interface of CallLowering::lowerCall to accept several
virtual registers for the call result, instead of just one.  This is a
follow-up to D46018.

CallLowering::lowerReturn was similarly refactored in D49660 and
lowerFormalArguments in D63549.

With this change, we no longer pack the virtual registers generated for
aggregates into one big lump before delegating to the target. Therefore,
the target can decide itself whether it wants to handle them as separate
pieces or use one big register.

ARM and AArch64 have been updated to use the passed in virtual registers
directly, which means we no longer need to generate so many
merge/extract instructions.

NFCI for AMDGPU, Mips and X86.

Differential Revision: https://reviews.llvm.org/D63550

llvm-svn: 364511
This commit is contained in:
Diana Picus 2019-06-27 09:15:53 +00:00
parent c3dbe23977
commit 8138996128
10 changed files with 41 additions and 87 deletions

View File

@ -258,8 +258,9 @@ public:
/// ///
/// \p CI is the call/invoke instruction. /// \p CI is the call/invoke instruction.
/// ///
/// \p ResReg is a register where the call's return value should be stored (or /// \p ResRegs are the registers where the call's return value should be
/// 0 if there is no return value). /// stored (or 0 if there is no return value). There will be one register for
/// each non-aggregate type, as returned by \c computeValueLLTs.
/// ///
/// \p ArgRegs is a list of virtual registers containing each argument that /// \p ArgRegs is a list of virtual registers containing each argument that
/// needs to be passed. /// needs to be passed.
@ -275,10 +276,9 @@ public:
/// ///
/// \return true if the lowering succeeded, false otherwise. /// \return true if the lowering succeeded, false otherwise.
bool lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS, bool lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS,
Register ResReg, ArrayRef<Register> ArgRegs, ArrayRef<Register> ResRegs, ArrayRef<Register> ArgRegs,
Register SwiftErrorVReg, Register SwiftErrorVReg,
std::function<unsigned()> GetCalleeReg) const; std::function<unsigned()> GetCalleeReg) const;
}; };
} // end namespace llvm } // end namespace llvm

View File

@ -28,7 +28,8 @@ using namespace llvm;
void CallLowering::anchor() {} void CallLowering::anchor() {}
bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS, bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS,
Register ResReg, ArrayRef<Register> ArgRegs, ArrayRef<Register> ResRegs,
ArrayRef<Register> ArgRegs,
Register SwiftErrorVReg, Register SwiftErrorVReg,
std::function<unsigned()> GetCalleeReg) const { std::function<unsigned()> GetCalleeReg) const {
auto &DL = CS.getParent()->getParent()->getParent()->getDataLayout(); auto &DL = CS.getParent()->getParent()->getParent()->getDataLayout();
@ -56,7 +57,7 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, ImmutableCallSite CS,
else else
Callee = MachineOperand::CreateReg(GetCalleeReg(), false); Callee = MachineOperand::CreateReg(GetCalleeReg(), false);
ArgInfo OrigRet{ResReg, CS.getType(), ISD::ArgFlagsTy{}}; ArgInfo OrigRet{ResRegs, CS.getType(), ISD::ArgFlagsTy{}};
if (!OrigRet.Ty->isVoidTy()) if (!OrigRet.Ty->isVoidTy())
setArgFlags(OrigRet, AttributeList::ReturnIndex, DL, CS); setArgFlags(OrigRet, AttributeList::ReturnIndex, DL, CS);

View File

@ -1585,10 +1585,7 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
} }
if (!F || !F->isIntrinsic() || ID == Intrinsic::not_intrinsic) { if (!F || !F->isIntrinsic() || ID == Intrinsic::not_intrinsic) {
bool IsSplitType = valueIsSplit(CI); ArrayRef<Register> Res = getOrCreateVRegs(CI);
Register Res = IsSplitType ? MRI->createGenericVirtualRegister(
getLLTForType(*CI.getType(), *DL))
: getOrCreateVReg(CI);
SmallVector<Register, 8> Args; SmallVector<Register, 8> Args;
Register SwiftErrorVReg; Register SwiftErrorVReg;
@ -1611,9 +1608,6 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
CLI->lowerCall(MIRBuilder, &CI, Res, Args, SwiftErrorVReg, CLI->lowerCall(MIRBuilder, &CI, Res, Args, SwiftErrorVReg,
[&]() { return getOrCreateVReg(*CI.getCalledValue()); }); [&]() { return getOrCreateVReg(*CI.getCalledValue()); });
if (IsSplitType)
unpackRegs(CI, Res, MIRBuilder);
return Success; return Success;
} }
@ -1687,11 +1681,11 @@ bool IRTranslator::translateInvoke(const User &U,
MCSymbol *BeginSymbol = Context.createTempSymbol(); MCSymbol *BeginSymbol = Context.createTempSymbol();
MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(BeginSymbol); MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(BeginSymbol);
Register Res; ArrayRef<Register> Res;
if (!I.getType()->isVoidTy()) if (!I.getType()->isVoidTy())
Res = MRI->createGenericVirtualRegister(getLLTForType(*I.getType(), *DL)); Res = getOrCreateVRegs(I);
SmallVector<Register, 8> Args; SmallVector<Register, 8> Args;
Register SwiftErrorVReg; Register SwiftErrorVReg = 0;
for (auto &Arg : I.arg_operands()) { for (auto &Arg : I.arg_operands()) {
if (CLI->supportSwiftError() && isSwiftError(Arg)) { if (CLI->supportSwiftError() && isSwiftError(Arg)) {
LLT Ty = getLLTForType(*Arg->getType(), *DL); LLT Ty = getLLTForType(*Arg->getType(), *DL);
@ -1711,8 +1705,6 @@ bool IRTranslator::translateInvoke(const User &U,
[&]() { return getOrCreateVReg(*I.getCalledValue()); })) [&]() { return getOrCreateVReg(*I.getCalledValue()); }))
return false; return false;
unpackRegs(I, Res, MIRBuilder);
MCSymbol *EndSymbol = Context.createTempSymbol(); MCSymbol *EndSymbol = Context.createTempSymbol();
MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(EndSymbol); MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(EndSymbol);

View File

@ -498,24 +498,18 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
// symmetry with the arugments, the physical register must be an // symmetry with the arugments, the physical register must be an
// implicit-define of the call instruction. // implicit-define of the call instruction.
CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(F.getCallingConv()); CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(F.getCallingConv());
assert(OrigRet.Regs.size() == 1 && "Can't handle multple regs yet"); if (!OrigRet.Ty->isVoidTy()) {
if (OrigRet.Regs[0]) {
SplitArgs.clear(); SplitArgs.clear();
SmallVector<uint64_t, 8> RegOffsets;
SmallVector<Register, 8> SplitRegs;
splitToValueTypes(OrigRet, SplitArgs, DL, MRI, F.getCallingConv(), splitToValueTypes(OrigRet, SplitArgs, DL, MRI, F.getCallingConv(),
[&](unsigned Reg, uint64_t Offset) { [&](unsigned Reg, uint64_t Offset) {
RegOffsets.push_back(Offset); llvm_unreachable(
SplitRegs.push_back(Reg); "Call results should already be split");
}); });
CallReturnHandler Handler(MIRBuilder, MRI, MIB, RetAssignFn); CallReturnHandler Handler(MIRBuilder, MRI, MIB, RetAssignFn);
if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) if (!handleAssignments(MIRBuilder, SplitArgs, Handler))
return false; return false;
if (!RegOffsets.empty())
MIRBuilder.buildSequence(OrigRet.Regs[0], SplitRegs, RegOffsets);
} }
if (SwiftErrorVReg) { if (SwiftErrorVReg) {

View File

@ -619,21 +619,14 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
return false; return false;
ArgInfos.clear(); ArgInfos.clear();
SmallVector<Register, 8> SplitRegs; splitToValueTypes(OrigRet, ArgInfos, MF, [&](Register Reg) {
splitToValueTypes(OrigRet, ArgInfos, MF, llvm_unreachable("Call results should already be split");
[&](Register Reg) { SplitRegs.push_back(Reg); }); });
auto RetAssignFn = TLI.CCAssignFnForReturn(CallConv, IsVarArg); auto RetAssignFn = TLI.CCAssignFnForReturn(CallConv, IsVarArg);
CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn); CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn);
if (!handleAssignments(MIRBuilder, ArgInfos, RetHandler)) if (!handleAssignments(MIRBuilder, ArgInfos, RetHandler))
return false; return false;
if (!SplitRegs.empty()) {
// We have split the value and allocated each individual piece, now build
// it up again.
assert(OrigRet.Regs.size() == 1 && "Can't handle multple regs yet");
MIRBuilder.buildMerge(OrigRet.Regs[0], SplitRegs);
}
} }
// We now know the size of the stack - update the ADJCALLSTACKDOWN // We now know the size of the stack - update the ADJCALLSTACKDOWN

View File

@ -369,7 +369,7 @@ bool ARMLegalizerInfo::legalizeCustom(MachineInstr &MI,
return false; return false;
case G_SREM: case G_SREM:
case G_UREM: { case G_UREM: {
unsigned OriginalResult = MI.getOperand(0).getReg(); Register OriginalResult = MI.getOperand(0).getReg();
auto Size = MRI.getType(OriginalResult).getSizeInBits(); auto Size = MRI.getType(OriginalResult).getSizeInBits();
if (Size != 32) if (Size != 32)
return false; return false;
@ -378,24 +378,17 @@ bool ARMLegalizerInfo::legalizeCustom(MachineInstr &MI,
MI.getOpcode() == G_SREM ? RTLIB::SDIVREM_I32 : RTLIB::UDIVREM_I32; MI.getOpcode() == G_SREM ? RTLIB::SDIVREM_I32 : RTLIB::UDIVREM_I32;
// Our divmod libcalls return a struct containing the quotient and the // Our divmod libcalls return a struct containing the quotient and the
// remainder. We need to create a virtual register for it. // remainder. Create a new, unused register for the quotient and use the
// destination of the original instruction for the remainder.
Type *ArgTy = Type::getInt32Ty(Ctx); Type *ArgTy = Type::getInt32Ty(Ctx);
StructType *RetTy = StructType::get(Ctx, {ArgTy, ArgTy}, /* Packed */ true); StructType *RetTy = StructType::get(Ctx, {ArgTy, ArgTy}, /* Packed */ true);
auto RetVal = MRI.createGenericVirtualRegister( Register RetRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
getLLTForType(*RetTy, MIRBuilder.getMF().getDataLayout())); OriginalResult};
auto Status = createLibcall(MIRBuilder, Libcall, {RetRegs, RetTy},
auto Status = createLibcall(MIRBuilder, Libcall, {RetVal, RetTy},
{{MI.getOperand(1).getReg(), ArgTy}, {{MI.getOperand(1).getReg(), ArgTy},
{MI.getOperand(2).getReg(), ArgTy}}); {MI.getOperand(2).getReg(), ArgTy}});
if (Status != LegalizerHelper::Legalized) if (Status != LegalizerHelper::Legalized)
return false; return false;
// The remainder is the second result of divmod. Split the return value into
// a new, unused register for the quotient and the destination of the
// original instruction for the remainder.
MIRBuilder.buildUnmerge(
{MRI.createGenericVirtualRegister(LLT::scalar(32)), OriginalResult},
RetVal);
break; break;
} }
case G_FCMP: { case G_FCMP: {

View File

@ -514,7 +514,6 @@ bool MipsCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
return false; return false;
} }
assert(OrigRet.Regs.size() == 1 && "Can't handle multple regs yet");
if (OrigRet.Regs[0] && !isSupportedType(OrigRet.Ty)) if (OrigRet.Regs[0] && !isSupportedType(OrigRet.Ty))
return false; return false;

View File

@ -453,7 +453,8 @@ bool X86CallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
// implicit-define of the call instruction. // implicit-define of the call instruction.
if (!OrigRet.Ty->isVoidTy()) { if (!OrigRet.Ty->isVoidTy()) {
assert(OrigRet.Regs.size() == 1 && "Can't handle multple regs yet"); if (OrigRet.Regs.size() > 1)
return false;
SplitArgs.clear(); SplitArgs.clear();
SmallVector<Register, 8> NewRegs; SmallVector<Register, 8> NewRegs;

View File

@ -131,8 +131,7 @@ define {double, i64, i32} @test_struct_return({double, i64, i32}* %addr) {
; CHECK: [[E1:%[0-9]+]]:_(s64) = COPY $x1 ; CHECK: [[E1:%[0-9]+]]:_(s64) = COPY $x1
; CHECK: [[E2:%[0-9]+]]:_(s64) = COPY $x2 ; CHECK: [[E2:%[0-9]+]]:_(s64) = COPY $x2
; CHECK: [[E3:%[0-9]+]]:_(s64) = COPY $x3 ; CHECK: [[E3:%[0-9]+]]:_(s64) = COPY $x3
; CHECK: [[RES:%[0-9]+]]:_(s256) = G_MERGE_VALUES [[E0]](s64), [[E1]](s64), [[E2]](s64), [[E3]](s64) ; CHECK: $x0 = COPY [[E1]]
; CHECK: G_EXTRACT [[RES]](s256), 64
declare [4 x i64] @arr_callee([4 x i64]) declare [4 x i64] @arr_callee([4 x i64])
define i64 @test_arr_call([4 x i64]* %addr) { define i64 @test_arr_call([4 x i64]* %addr) {
%arg = load [4 x i64], [4 x i64]* %addr %arg = load [4 x i64], [4 x i64]* %addr

View File

@ -214,17 +214,13 @@ define arm_aapcscc [3 x i32] @test_tiny_int_arrays([2 x i32] %arr) {
; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0 ; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0
; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1 ; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1
; CHECK: [[R2:%[0-9]+]]:_(s32) = COPY $r2 ; CHECK: [[R2:%[0-9]+]]:_(s32) = COPY $r2
; CHECK: [[RES_ARR:%[0-9]+]]:_(s96) = G_MERGE_VALUES [[R0]](s32), [[R1]](s32), [[R2]](s32)
; CHECK: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp ; CHECK: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp
; CHECK: [[EXT3:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 0
; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 32
; CHECK: [[EXT5:%[0-9]+]]:_(s32) = G_EXTRACT [[RES_ARR]](s96), 64
; FIXME: This doesn't seem correct with regard to the AAPCS docs (which say ; FIXME: This doesn't seem correct with regard to the AAPCS docs (which say
; that composite types larger than 4 bytes should be passed through memory), ; that composite types larger than 4 bytes should be passed through memory),
; but it's what DAGISel does. We should fix it in the common code for both. ; but it's what DAGISel does. We should fix it in the common code for both.
; CHECK: $r0 = COPY [[EXT3]] ; CHECK: $r0 = COPY [[R0]]
; CHECK: $r1 = COPY [[EXT4]] ; CHECK: $r1 = COPY [[R1]]
; CHECK: $r2 = COPY [[EXT5]] ; CHECK: $r2 = COPY [[R2]]
; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1, implicit $r2 ; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1, implicit $r2
; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1, implicit $r2 ; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1, implicit $r2
entry: entry:
@ -352,12 +348,9 @@ define arm_aapcscc [2 x float] @test_fp_arrays_aapcs([3 x double] %arr) {
; THUMB: tBL 14, $noreg, @fp_arrays_aapcs_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1 ; THUMB: tBL 14, $noreg, @fp_arrays_aapcs_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1
; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0 ; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0
; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1 ; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1
; CHECK: [[R_MERGED:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[R0]](s32), [[R1]](s32)
; CHECK: ADJCALLSTACKUP 8, 0, 14, $noreg, implicit-def $sp, implicit $sp ; CHECK: ADJCALLSTACKUP 8, 0, 14, $noreg, implicit-def $sp, implicit $sp
; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s64), 0 ; CHECK: $r0 = COPY [[R0]]
; CHECK: [[EXT5:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s64), 32 ; CHECK: $r1 = COPY [[R1]]
; CHECK: $r0 = COPY [[EXT4]]
; CHECK: $r1 = COPY [[EXT5]]
; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1 ; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1
; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1 ; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1
entry: entry:
@ -434,16 +427,11 @@ define arm_aapcs_vfpcc [4 x float] @test_fp_arrays_aapcs_vfp([3 x double] %x, [3
; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $s1 ; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $s1
; CHECK: [[R2:%[0-9]+]]:_(s32) = COPY $s2 ; CHECK: [[R2:%[0-9]+]]:_(s32) = COPY $s2
; CHECK: [[R3:%[0-9]+]]:_(s32) = COPY $s3 ; CHECK: [[R3:%[0-9]+]]:_(s32) = COPY $s3
; CHECK: [[R_MERGED:%[0-9]+]]:_(s128) = G_MERGE_VALUES [[R0]](s32), [[R1]](s32), [[R2]](s32), [[R3]](s32)
; CHECK: ADJCALLSTACKUP 32, 0, 14, $noreg, implicit-def $sp, implicit $sp ; CHECK: ADJCALLSTACKUP 32, 0, 14, $noreg, implicit-def $sp, implicit $sp
; CHECK: [[EXT11:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 0 ; CHECK: $s0 = COPY [[R0]]
; CHECK: [[EXT12:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 32 ; CHECK: $s1 = COPY [[R1]]
; CHECK: [[EXT13:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 64 ; CHECK: $s2 = COPY [[R2]]
; CHECK: [[EXT14:%[0-9]+]]:_(s32) = G_EXTRACT [[R_MERGED]](s128), 96 ; CHECK: $s3 = COPY [[R3]]
; CHECK: $s0 = COPY [[EXT11]]
; CHECK: $s1 = COPY [[EXT12]]
; CHECK: $s2 = COPY [[EXT13]]
; CHECK: $s3 = COPY [[EXT14]]
; ARM: BX_RET 14, $noreg, implicit $s0, implicit $s1, implicit $s2, implicit $s3 ; ARM: BX_RET 14, $noreg, implicit $s0, implicit $s1, implicit $s2, implicit $s3
; THUMB: tBX_RET 14, $noreg, implicit $s0, implicit $s1, implicit $s2, implicit $s3 ; THUMB: tBX_RET 14, $noreg, implicit $s0, implicit $s1, implicit $s2, implicit $s3
entry: entry:
@ -488,14 +476,11 @@ define arm_aapcscc [2 x i32*] @test_tough_arrays([6 x [4 x i32]] %arr) {
; CHECK: G_STORE [[LAST_STACK_ELEMENT]](s32), [[LAST_STACK_ARG_ADDR]]{{.*}}store 4 ; CHECK: G_STORE [[LAST_STACK_ELEMENT]](s32), [[LAST_STACK_ARG_ADDR]]{{.*}}store 4
; ARM: BL @tough_arrays_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1 ; ARM: BL @tough_arrays_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1
; THUMB: tBL 14, $noreg, @tough_arrays_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1 ; THUMB: tBL 14, $noreg, @tough_arrays_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit $r2, implicit $r3, implicit-def $r0, implicit-def $r1
; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0 ; CHECK: [[R0:%[0-9]+]]:_(p0) = COPY $r0
; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1 ; CHECK: [[R1:%[0-9]+]]:_(p0) = COPY $r1
; CHECK: [[RES_ARR:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[R0]](s32), [[R1]](s32)
; CHECK: ADJCALLSTACKUP 80, 0, 14, $noreg, implicit-def $sp, implicit $sp ; CHECK: ADJCALLSTACKUP 80, 0, 14, $noreg, implicit-def $sp, implicit $sp
; CHECK: [[EXT1:%[0-9]+]]:_(p0) = G_EXTRACT [[RES_ARR]](s64), 0 ; CHECK: $r0 = COPY [[R0]]
; CHECK: [[EXT2:%[0-9]+]]:_(p0) = G_EXTRACT [[RES_ARR]](s64), 32 ; CHECK: $r1 = COPY [[R1]]
; CHECK: $r0 = COPY [[EXT1]]
; CHECK: $r1 = COPY [[EXT2]]
; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1 ; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1
; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1 ; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1
entry: entry:
@ -521,12 +506,9 @@ define arm_aapcscc {i32, i32} @test_structs({i32, i32} %x) {
; THUMB: tBL 14, $noreg, @structs_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit-def $r0, implicit-def $r1 ; THUMB: tBL 14, $noreg, @structs_target, csr_aapcs, implicit-def $lr, implicit $sp, implicit $r0, implicit $r1, implicit-def $r0, implicit-def $r1
; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0 ; CHECK: [[R0:%[0-9]+]]:_(s32) = COPY $r0
; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1 ; CHECK: [[R1:%[0-9]+]]:_(s32) = COPY $r1
; CHECK: [[R:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[R0]](s32), [[R1]](s32)
; CHECK: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp ; CHECK: ADJCALLSTACKUP 0, 0, 14, $noreg, implicit-def $sp, implicit $sp
; CHECK: [[EXT3:%[0-9]+]]:_(s32) = G_EXTRACT [[R]](s64), 0 ; CHECK: $r0 = COPY [[R0]](s32)
; CHECK: [[EXT4:%[0-9]+]]:_(s32) = G_EXTRACT [[R]](s64), 32 ; CHECK: $r1 = COPY [[R1]](s32)
; CHECK: $r0 = COPY [[EXT3]](s32)
; CHECK: $r1 = COPY [[EXT4]](s32)
; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1 ; ARM: BX_RET 14, $noreg, implicit $r0, implicit $r1
; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1 ; THUMB: tBX_RET 14, $noreg, implicit $r0, implicit $r1
%r = notail call arm_aapcscc {i32, i32} @structs_target({i32, i32} %x) %r = notail call arm_aapcscc {i32, i32} @structs_target({i32, i32} %x)