mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-29 06:30:30 +00:00
Some ARM implementaions, e.g. A-series, does return stack prediction. That is,
the processor keeps a return addresses stack (RAS) which stores the address and the instruction execution state of the instruction after a function-call type branch instruction. Calling a "noreturn" function with normal call instructions (e.g. bl) can corrupt RAS and causes 100% return misprediction so LLVM should use a unconditional branch instead. i.e. mov lr, pc b _foo The "mov lr, pc" is issued in order to get proper backtrace. rdar://8979299 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@151623 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
1f59523521
commit
ec52aaa12f
@ -1233,7 +1233,8 @@ public:
|
||||
LowerCallTo(SDValue Chain, Type *RetTy, bool RetSExt, bool RetZExt,
|
||||
bool isVarArg, bool isInreg, unsigned NumFixedArgs,
|
||||
CallingConv::ID CallConv, bool isTailCall,
|
||||
bool isReturnValueUsed, SDValue Callee, ArgListTy &Args,
|
||||
bool doesNotRet, bool isReturnValueUsed,
|
||||
SDValue Callee, ArgListTy &Args,
|
||||
SelectionDAG &DAG, DebugLoc dl) const;
|
||||
|
||||
/// LowerCall - This hook must be implemented to lower calls into the
|
||||
@ -1245,7 +1246,7 @@ public:
|
||||
virtual SDValue
|
||||
LowerCall(SDValue /*Chain*/, SDValue /*Callee*/,
|
||||
CallingConv::ID /*CallConv*/, bool /*isVarArg*/,
|
||||
bool &/*isTailCall*/,
|
||||
bool /*doesNotRet*/, bool &/*isTailCall*/,
|
||||
const SmallVectorImpl<ISD::OutputArg> &/*Outs*/,
|
||||
const SmallVectorImpl<SDValue> &/*OutVals*/,
|
||||
const SmallVectorImpl<ISD::InputArg> &/*Ins*/,
|
||||
|
@ -1788,7 +1788,7 @@ SDValue SelectionDAGLegalize::ExpandLibCall(RTLIB::Libcall LC, SDNode *Node,
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), isTailCall,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, Node->getDebugLoc());
|
||||
|
||||
if (!CallInfo.second.getNode())
|
||||
@ -1821,7 +1821,7 @@ SDValue SelectionDAGLegalize::ExpandLibCall(RTLIB::Libcall LC, EVT RetVT,
|
||||
std::pair<SDValue,SDValue> CallInfo =
|
||||
TLI.LowerCallTo(DAG.getEntryNode(), RetTy, isSigned, !isSigned, false,
|
||||
false, 0, TLI.getLibcallCallingConv(LC), false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, dl);
|
||||
|
||||
return CallInfo.first;
|
||||
@ -1853,7 +1853,7 @@ SelectionDAGLegalize::ExpandChainLibCall(RTLIB::Libcall LC,
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), /*isTailCall=*/false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, Node->getDebugLoc());
|
||||
|
||||
return CallInfo;
|
||||
@ -1985,7 +1985,8 @@ SelectionDAGLegalize::ExpandDivRemLibCall(SDNode *Node,
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), /*isTailCall=*/false,
|
||||
/*isReturnValueUsed=*/true, Callee, Args, DAG, dl);
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, dl);
|
||||
|
||||
// Remainder is loaded back from the stack frame.
|
||||
SDValue Rem = DAG.getLoad(RetVT, dl, CallInfo.second, FIPtr,
|
||||
@ -2563,7 +2564,7 @@ void SelectionDAGLegalize::ExpandNode(SDNode *Node) {
|
||||
TLI.LowerCallTo(Node->getOperand(0), Type::getVoidTy(*DAG.getContext()),
|
||||
false, false, false, false, 0, CallingConv::C,
|
||||
/*isTailCall=*/false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("__sync_synchronize",
|
||||
TLI.getPointerTy()),
|
||||
Args, DAG, dl);
|
||||
@ -2640,7 +2641,7 @@ void SelectionDAGLegalize::ExpandNode(SDNode *Node) {
|
||||
TLI.LowerCallTo(Node->getOperand(0), Type::getVoidTy(*DAG.getContext()),
|
||||
false, false, false, false, 0, CallingConv::C,
|
||||
/*isTailCall=*/false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("abort", TLI.getPointerTy()),
|
||||
Args, DAG, dl);
|
||||
Results.push_back(CallResult.second);
|
||||
|
@ -2311,8 +2311,10 @@ void DAGTypeLegalizer::ExpandIntRes_XMULO(SDNode *N,
|
||||
SDValue Func = DAG.getExternalSymbol(TLI.getLibcallName(LC), PtrVT);
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(Chain, RetTy, true, false, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), false,
|
||||
true, Func, Args, DAG, dl);
|
||||
0, TLI.getLibcallCallingConv(LC),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Func, Args, DAG, dl);
|
||||
|
||||
SplitInteger(CallInfo.first, Lo, Hi);
|
||||
SDValue Temp2 = DAG.getLoad(PtrVT, dl, CallInfo.second, Temp,
|
||||
|
@ -1056,8 +1056,9 @@ SDValue DAGTypeLegalizer::MakeLibCall(RTLIB::Libcall LC, EVT RetVT,
|
||||
Type *RetTy = RetVT.getTypeForEVT(*DAG.getContext());
|
||||
std::pair<SDValue,SDValue> CallInfo =
|
||||
TLI.LowerCallTo(DAG.getEntryNode(), RetTy, isSigned, !isSigned, false,
|
||||
false, 0, TLI.getLibcallCallingConv(LC), false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
false, 0, TLI.getLibcallCallingConv(LC),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, dl);
|
||||
return CallInfo.first;
|
||||
}
|
||||
@ -1088,7 +1089,7 @@ DAGTypeLegalizer::ExpandChainLibCall(RTLIB::Libcall LC,
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), /*isTailCall=*/false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, Node->getDebugLoc());
|
||||
|
||||
return CallInfo;
|
||||
|
@ -3736,8 +3736,9 @@ SDValue SelectionDAG::getMemcpy(SDValue Chain, DebugLoc dl, SDValue Dst,
|
||||
std::pair<SDValue,SDValue> CallResult =
|
||||
TLI.LowerCallTo(Chain, Type::getVoidTy(*getContext()),
|
||||
false, false, false, false, 0,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMCPY), false,
|
||||
/*isReturnValueUsed=*/false,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMCPY),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/false,
|
||||
getExternalSymbol(TLI.getLibcallName(RTLIB::MEMCPY),
|
||||
TLI.getPointerTy()),
|
||||
Args, *this, dl);
|
||||
@ -3788,8 +3789,9 @@ SDValue SelectionDAG::getMemmove(SDValue Chain, DebugLoc dl, SDValue Dst,
|
||||
std::pair<SDValue,SDValue> CallResult =
|
||||
TLI.LowerCallTo(Chain, Type::getVoidTy(*getContext()),
|
||||
false, false, false, false, 0,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMMOVE), false,
|
||||
/*isReturnValueUsed=*/false,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMMOVE),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotReturn=*/false, /*isReturnValueUsed=*/false,
|
||||
getExternalSymbol(TLI.getLibcallName(RTLIB::MEMMOVE),
|
||||
TLI.getPointerTy()),
|
||||
Args, *this, dl);
|
||||
@ -3848,8 +3850,9 @@ SDValue SelectionDAG::getMemset(SDValue Chain, DebugLoc dl, SDValue Dst,
|
||||
std::pair<SDValue,SDValue> CallResult =
|
||||
TLI.LowerCallTo(Chain, Type::getVoidTy(*getContext()),
|
||||
false, false, false, false, 0,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMSET), false,
|
||||
/*isReturnValueUsed=*/false,
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMSET),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotReturn*/false, /*isReturnValueUsed=*/false,
|
||||
getExternalSymbol(TLI.getLibcallName(RTLIB::MEMSET),
|
||||
TLI.getPointerTy()),
|
||||
Args, *this, dl);
|
||||
|
@ -5081,7 +5081,8 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||
std::pair<SDValue, SDValue> Result =
|
||||
TLI.LowerCallTo(getRoot(), I.getType(),
|
||||
false, false, false, false, 0, CallingConv::C,
|
||||
/*isTailCall=*/false, /*isReturnValueUsed=*/true,
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol(TrapFuncName.data(), TLI.getPointerTy()),
|
||||
Args, DAG, getCurDebugLoc());
|
||||
DAG.setRoot(Result.second);
|
||||
@ -5244,6 +5245,7 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee,
|
||||
CS.paramHasAttr(0, Attribute::InReg), FTy->getNumParams(),
|
||||
CS.getCallingConv(),
|
||||
isTailCall,
|
||||
CS.doesNotReturn(),
|
||||
!CS.getInstruction()->use_empty(),
|
||||
Callee, Args, DAG, getCurDebugLoc());
|
||||
assert((isTailCall || Result.second.getNode()) &&
|
||||
@ -6360,7 +6362,7 @@ TargetLowering::LowerCallTo(SDValue Chain, Type *RetTy,
|
||||
bool RetSExt, bool RetZExt, bool isVarArg,
|
||||
bool isInreg, unsigned NumFixedArgs,
|
||||
CallingConv::ID CallConv, bool isTailCall,
|
||||
bool isReturnValueUsed,
|
||||
bool doesNotRet, bool isReturnValueUsed,
|
||||
SDValue Callee,
|
||||
ArgListTy &Args, SelectionDAG &DAG,
|
||||
DebugLoc dl) const {
|
||||
@ -6457,7 +6459,7 @@ TargetLowering::LowerCallTo(SDValue Chain, Type *RetTy,
|
||||
}
|
||||
|
||||
SmallVector<SDValue, 4> InVals;
|
||||
Chain = LowerCall(Chain, Callee, CallConv, isVarArg, isTailCall,
|
||||
Chain = LowerCall(Chain, Callee, CallConv, isVarArg, doesNotRet, isTailCall,
|
||||
Outs, OutVals, Ins, dl, DAG, InVals);
|
||||
|
||||
// Verify that the target's LowerCall behaved as expected.
|
||||
|
@ -89,6 +89,11 @@ def FeatureAvoidPartialCPSR : SubtargetFeature<"avoid-partial-cpsr",
|
||||
"AvoidCPSRPartialUpdate", "true",
|
||||
"Avoid CPSR partial update for OOO execution">;
|
||||
|
||||
// Some processors perform return stack prediction. CodeGen should avoid issue
|
||||
// "normal" call instructions to callees which do not return.
|
||||
def FeatureHasRAS : SubtargetFeature<"ras", "HasRAS", "true",
|
||||
"Has return address stack">;
|
||||
|
||||
/// Some M architectures don't have the DSP extension (v7E-M vs. v7M)
|
||||
def FeatureDSPThumb2 : SubtargetFeature<"t2dsp", "Thumb2DSP", "true",
|
||||
"Supports v7 DSP instructions in Thumb2">;
|
||||
@ -204,13 +209,14 @@ def : Processor<"arm1156t2f-s", ARMV6Itineraries, [HasV6T2Ops, FeatureVFP2,
|
||||
// V7a Processors.
|
||||
def : Processor<"cortex-a8", CortexA8Itineraries,
|
||||
[ProcA8, HasV7Ops, FeatureNEON, FeatureDB,
|
||||
FeatureDSPThumb2]>;
|
||||
FeatureDSPThumb2, FeatureHasRAS]>;
|
||||
def : Processor<"cortex-a9", CortexA9Itineraries,
|
||||
[ProcA9, HasV7Ops, FeatureNEON, FeatureDB,
|
||||
FeatureDSPThumb2]>;
|
||||
FeatureDSPThumb2, FeatureHasRAS]>;
|
||||
def : Processor<"cortex-a9-mp", CortexA9Itineraries,
|
||||
[ProcA9, HasV7Ops, FeatureNEON, FeatureDB,
|
||||
FeatureDSPThumb2, FeatureMP]>;
|
||||
FeatureDSPThumb2, FeatureMP,
|
||||
FeatureHasRAS]>;
|
||||
|
||||
// V7M Processors.
|
||||
def : ProcNoItin<"cortex-m3", [HasV7Ops,
|
||||
|
@ -1343,6 +1343,60 @@ void ARMAsmPrinter::EmitInstruction(const MachineInstr *MI) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ARM::BMOVPCBr9_CALL:
|
||||
case ARM::BMOVPCB_CALL: {
|
||||
{
|
||||
MCInst TmpInst;
|
||||
TmpInst.setOpcode(ARM::MOVr);
|
||||
TmpInst.addOperand(MCOperand::CreateReg(ARM::LR));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(ARM::PC));
|
||||
// Add predicate operands.
|
||||
TmpInst.addOperand(MCOperand::CreateImm(ARMCC::AL));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(0));
|
||||
// Add 's' bit operand (always reg0 for this)
|
||||
TmpInst.addOperand(MCOperand::CreateReg(0));
|
||||
OutStreamer.EmitInstruction(TmpInst);
|
||||
}
|
||||
{
|
||||
MCInst TmpInst;
|
||||
TmpInst.setOpcode(ARM::Bcc);
|
||||
const GlobalValue *GV = MI->getOperand(0).getGlobal();
|
||||
MCSymbol *GVSym = Mang->getSymbol(GV);
|
||||
const MCExpr *GVSymExpr = MCSymbolRefExpr::Create(GVSym, OutContext);
|
||||
TmpInst.addOperand(MCOperand::CreateExpr(GVSymExpr));
|
||||
// Add predicate operands.
|
||||
TmpInst.addOperand(MCOperand::CreateImm(ARMCC::AL));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(0));
|
||||
OutStreamer.EmitInstruction(TmpInst);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ARM::t2BMOVPCBr9_CALL:
|
||||
case ARM::t2BMOVPCB_CALL: {
|
||||
{
|
||||
MCInst TmpInst;
|
||||
TmpInst.setOpcode(ARM::tMOVr);
|
||||
TmpInst.addOperand(MCOperand::CreateReg(ARM::LR));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(ARM::PC));
|
||||
// Add predicate operands.
|
||||
TmpInst.addOperand(MCOperand::CreateImm(ARMCC::AL));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(0));
|
||||
OutStreamer.EmitInstruction(TmpInst);
|
||||
}
|
||||
{
|
||||
MCInst TmpInst;
|
||||
TmpInst.setOpcode(ARM::t2B);
|
||||
const GlobalValue *GV = MI->getOperand(0).getGlobal();
|
||||
MCSymbol *GVSym = Mang->getSymbol(GV);
|
||||
const MCExpr *GVSymExpr = MCSymbolRefExpr::Create(GVSym, OutContext);
|
||||
TmpInst.addOperand(MCOperand::CreateExpr(GVSymExpr));
|
||||
// Add predicate operands.
|
||||
TmpInst.addOperand(MCOperand::CreateImm(ARMCC::AL));
|
||||
TmpInst.addOperand(MCOperand::CreateReg(0));
|
||||
OutStreamer.EmitInstruction(TmpInst);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ARM::MOVi16_ga_pcrel:
|
||||
case ARM::t2MOVi16_ga_pcrel: {
|
||||
MCInst TmpInst;
|
||||
|
@ -1286,7 +1286,7 @@ void ARMTargetLowering::PassF64ArgInRegs(DebugLoc dl, SelectionDAG &DAG,
|
||||
SDValue
|
||||
ARMTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
@ -1582,12 +1582,20 @@ ARMTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
if (Subtarget->isThumb()) {
|
||||
if ((!isDirect || isARMFunc) && !Subtarget->hasV5TOps())
|
||||
CallOpc = ARMISD::CALL_NOLINK;
|
||||
else if (doesNotRet && !isARMFunc &&
|
||||
Subtarget->hasRAS() && !Subtarget->isThumb1Only())
|
||||
// "mov lr, pc; b _foo" to avoid confusing the RSP
|
||||
CallOpc = ARMISD::CALL_NOLINK;
|
||||
else
|
||||
CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL;
|
||||
} else {
|
||||
CallOpc = (isDirect || Subtarget->hasV5TOps())
|
||||
? (isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL)
|
||||
: ARMISD::CALL_NOLINK;
|
||||
if (!isDirect && !Subtarget->hasV5TOps()) {
|
||||
CallOpc = ARMISD::CALL_NOLINK;
|
||||
} else if (doesNotRet && Subtarget->hasRAS())
|
||||
// "mov lr, pc; b _foo" to avoid confusing the RSP
|
||||
CallOpc = ARMISD::CALL_NOLINK;
|
||||
else
|
||||
CallOpc = isLocalARMFunc ? ARMISD::CALL_PRED : ARMISD::CALL;
|
||||
}
|
||||
|
||||
std::vector<SDValue> Ops;
|
||||
@ -2080,7 +2088,8 @@ ARMTargetLowering::LowerToTLSGeneralDynamicModel(GlobalAddressSDNode *GA,
|
||||
std::pair<SDValue, SDValue> CallResult =
|
||||
LowerCallTo(Chain, (Type *) Type::getInt32Ty(*DAG.getContext()),
|
||||
false, false, false, false,
|
||||
0, CallingConv::C, false, /*isReturnValueUsed=*/true,
|
||||
0, CallingConv::C, /*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("__tls_get_addr", PtrVT), Args, DAG, dl);
|
||||
return CallResult.first;
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ namespace llvm {
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -1951,6 +1951,13 @@ let isCall = 1,
|
||||
def BMOVPCRX_CALL : ARMPseudoInst<(outs), (ins tGPR:$func, variable_ops),
|
||||
8, IIC_Br, [(ARMcall_nolink tGPR:$func)]>,
|
||||
Requires<[IsARM, NoV4T, IsNotIOS]>;
|
||||
|
||||
// mov lr, pc; b if callee is marked noreturn to avoid confusing the
|
||||
// return stack predictor.
|
||||
def BMOVPCB_CALL : ARMPseudoInst<(outs),
|
||||
(ins bl_target:$func, variable_ops),
|
||||
8, IIC_Br, [(ARMcall_nolink tglobaladdr:$func)]>,
|
||||
Requires<[IsARM, IsNotIOS]>;
|
||||
}
|
||||
|
||||
let isCall = 1,
|
||||
@ -1993,6 +2000,12 @@ let isCall = 1,
|
||||
def BMOVPCRXr9_CALL : ARMPseudoInst<(outs), (ins tGPR:$func, variable_ops),
|
||||
8, IIC_Br, [(ARMcall_nolink tGPR:$func)]>,
|
||||
Requires<[IsARM, NoV4T, IsIOS]>;
|
||||
|
||||
// mov lr, pc; b if callee is marked noreturn to avoid confusing the
|
||||
// return stack predictor.
|
||||
def BMOVPCBr9_CALL : ARMPseudoInst<(outs),(ins bl_target:$func, variable_ops),
|
||||
8, IIC_Br, [(ARMcall_nolink tglobaladdr:$func)]>,
|
||||
Requires<[IsARM, IsIOS]>;
|
||||
}
|
||||
|
||||
let isBranch = 1, isTerminator = 1 in {
|
||||
@ -4897,6 +4910,12 @@ def : ARMPat<(ARMcall texternalsym:$func), (BL texternalsym:$func)>,
|
||||
Requires<[IsARM, IsNotIOS]>;
|
||||
def : ARMPat<(ARMcall texternalsym:$func), (BLr9 texternalsym:$func)>,
|
||||
Requires<[IsARM, IsIOS]>;
|
||||
def : ARMPat<(ARMcall_nolink texternalsym:$func),
|
||||
(BMOVPCB_CALL texternalsym:$func)>,
|
||||
Requires<[IsARM, IsNotIOS]>;
|
||||
def : ARMPat<(ARMcall_nolink texternalsym:$func),
|
||||
(BMOVPCBr9_CALL texternalsym:$func)>,
|
||||
Requires<[IsARM, IsIOS]>;
|
||||
|
||||
// zextload i1 -> zextload i8
|
||||
def : ARMPat<(zextloadi1 addrmode_imm12:$addr), (LDRBi12 addrmode_imm12:$addr)>;
|
||||
|
@ -3277,6 +3277,38 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in {
|
||||
Requires<[IsThumb2, IsIOS]>;
|
||||
}
|
||||
|
||||
let isCall = 1,
|
||||
// On non-IOS platforms R9 is callee-saved.
|
||||
Defs = [LR], Uses = [SP] in {
|
||||
// mov lr, pc; b if callee is marked noreturn to avoid confusing the
|
||||
// return stack predictor.
|
||||
def t2BMOVPCB_CALL : tPseudoInst<(outs),
|
||||
(ins t_bltarget:$func, variable_ops),
|
||||
6, IIC_Br, [(ARMcall_nolink tglobaladdr:$func)]>,
|
||||
Requires<[IsThumb, IsNotIOS]>;
|
||||
}
|
||||
|
||||
let isCall = 1,
|
||||
// On IOS R9 is call-clobbered.
|
||||
// R7 is marked as a use to prevent frame-pointer assignments from being
|
||||
// moved above / below calls.
|
||||
Defs = [LR], Uses = [R7, SP] in {
|
||||
// mov lr, pc; b if callee is marked noreturn to avoid confusing the
|
||||
// return stack predictor.
|
||||
def t2BMOVPCBr9_CALL : tPseudoInst<(outs),
|
||||
(ins t_bltarget:$func, variable_ops),
|
||||
6, IIC_Br, [(ARMcall_nolink tglobaladdr:$func)]>,
|
||||
Requires<[IsThumb, IsIOS]>;
|
||||
}
|
||||
|
||||
// Direct calls
|
||||
def : T2Pat<(ARMcall_nolink texternalsym:$func),
|
||||
(t2BMOVPCB_CALL texternalsym:$func)>,
|
||||
Requires<[IsThumb, IsNotIOS]>;
|
||||
def : T2Pat<(ARMcall_nolink texternalsym:$func),
|
||||
(t2BMOVPCBr9_CALL texternalsym:$func)>,
|
||||
Requires<[IsThumb, IsIOS]>;
|
||||
|
||||
// IT block
|
||||
let Defs = [ITSTATE] in
|
||||
def t2IT : Thumb2XI<(outs), (ins it_pred:$cc, it_mask:$mask),
|
||||
|
@ -189,6 +189,7 @@ EmitTargetCodeForMemset(SelectionDAG &DAG, DebugLoc dl,
|
||||
0, // number of fixed arguments
|
||||
TLI.getLibcallCallingConv(RTLIB::MEMSET), // call conv
|
||||
false, // is tail call
|
||||
false, // does not return
|
||||
false, // is return val used
|
||||
DAG.getExternalSymbol(TLI.getLibcallName(RTLIB::MEMSET),
|
||||
TLI.getPointerTy()), // callee
|
||||
|
@ -125,6 +125,10 @@ protected:
|
||||
/// CPSR setting instruction.
|
||||
bool AvoidCPSRPartialUpdate;
|
||||
|
||||
/// HasRAS - Some processors perform return stack prediction. CodeGen should
|
||||
/// avoid issue "normal" call instructions to callees which do not return.
|
||||
bool HasRAS;
|
||||
|
||||
/// HasMPExtension - True if the subtarget supports Multiprocessing
|
||||
/// extension (ARMv7 only).
|
||||
bool HasMPExtension;
|
||||
@ -214,6 +218,7 @@ protected:
|
||||
bool isFPOnlySP() const { return FPOnlySP; }
|
||||
bool prefers32BitThumb() const { return Pref32BitThumb; }
|
||||
bool avoidCPSRPartialUpdate() const { return AvoidCPSRPartialUpdate; }
|
||||
bool hasRAS() const { return HasRAS; }
|
||||
bool hasMPExtension() const { return HasMPExtension; }
|
||||
bool hasThumb2DSP() const { return Thumb2DSP; }
|
||||
|
||||
|
@ -83,8 +83,9 @@ namespace {
|
||||
Op.getNode()->getValueType(0).getTypeForEVT(*DAG.getContext());
|
||||
std::pair<SDValue, SDValue> CallInfo =
|
||||
TLI.LowerCallTo(InChain, RetTy, isSigned, !isSigned, false, false,
|
||||
0, TLI.getLibcallCallingConv(LC), false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
0, TLI.getLibcallCallingConv(LC),
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
Callee, Args, DAG, Op.getDebugLoc());
|
||||
|
||||
return CallInfo.first;
|
||||
@ -1274,7 +1275,7 @@ static SDNode *isLSAAddress(SDValue Op, SelectionDAG &DAG) {
|
||||
SDValue
|
||||
SPUTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -162,7 +162,7 @@ namespace llvm {
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -371,7 +371,7 @@ HexagonTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag,
|
||||
SDValue
|
||||
HexagonTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -96,7 +96,7 @@ namespace llvm {
|
||||
|
||||
SDValue LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -682,7 +682,7 @@ static bool CC_MBlaze_AssignReg(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
|
||||
/// TODO: isVarArg, isTailCall.
|
||||
SDValue MBlazeTargetLowering::
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool &isTailCall,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -134,7 +134,7 @@ namespace llvm {
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -268,7 +268,7 @@ MSP430TargetLowering::LowerFormalArguments(SDValue Chain,
|
||||
SDValue
|
||||
MSP430TargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -152,8 +152,8 @@ namespace llvm {
|
||||
DebugLoc dl, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg, bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -1586,7 +1586,9 @@ LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const
|
||||
|
||||
std::pair<SDValue, SDValue> CallResult =
|
||||
LowerCallTo(DAG.getEntryNode(), PtrTy,
|
||||
false, false, false, false, 0, CallingConv::C, false, true,
|
||||
false, false, false, false, 0, CallingConv::C,
|
||||
/*isTailCall=*/false, /*doesNotRet=*/false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
TlsGetAddr, Args, DAG, dl);
|
||||
|
||||
SDValue Ret = CallResult.first;
|
||||
@ -2190,7 +2192,7 @@ PassByValArg64(SDValue& ByValChain, SDValue Chain, DebugLoc dl,
|
||||
SDValue
|
||||
MipsTargetLowering::LowerCall(SDValue InChain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -144,7 +144,7 @@ namespace llvm {
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -373,7 +373,7 @@ SDValue PTXTargetLowering::
|
||||
SDValue
|
||||
PTXTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -64,9 +64,8 @@ class PTXTargetLowering : public TargetLowering {
|
||||
SelectionDAG &DAG) const;
|
||||
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -1427,8 +1427,9 @@ SDValue PPCTargetLowering::LowerINIT_TRAMPOLINE(SDValue Op,
|
||||
// Lower to a call to __trampoline_setup(Trmp, TrampSize, FPtr, ctx_reg)
|
||||
std::pair<SDValue, SDValue> CallResult =
|
||||
LowerCallTo(Chain, Type::getVoidTy(*DAG.getContext()),
|
||||
false, false, false, false, 0, CallingConv::C, false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
false, false, false, false, 0, CallingConv::C,
|
||||
/*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("__trampoline_setup", PtrVT),
|
||||
Args, DAG, dl);
|
||||
|
||||
@ -2838,7 +2839,7 @@ PPCTargetLowering::FinishCall(CallingConv::ID CallConv, DebugLoc dl,
|
||||
SDValue
|
||||
PPCTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -437,8 +437,8 @@ namespace llvm {
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg, bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
@ -472,21 +472,21 @@ namespace llvm {
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
|
||||
SDValue
|
||||
LowerCall_Darwin(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg, bool isTailCall,
|
||||
LowerCall_Darwin(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
DebugLoc dl, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
SDValue
|
||||
LowerCall_SVR4(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg, bool isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
DebugLoc dl, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
LowerCall_SVR4(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
DebugLoc dl, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -347,7 +347,7 @@ SparcTargetLowering::LowerFormalArguments(SDValue Chain,
|
||||
SDValue
|
||||
SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -77,9 +77,8 @@ namespace llvm {
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -2119,7 +2119,7 @@ EmitTailCallStoreRetAddr(SelectionDAG & DAG, MachineFunction &MF,
|
||||
SDValue
|
||||
X86TargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -789,8 +789,8 @@ namespace llvm {
|
||||
DebugLoc dl, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg, bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -65,7 +65,8 @@ X86SelectionDAGInfo::EmitTargetCodeForMemset(SelectionDAG &DAG, DebugLoc dl,
|
||||
std::pair<SDValue,SDValue> CallResult =
|
||||
TLI.LowerCallTo(Chain, Type::getVoidTy(*DAG.getContext()),
|
||||
false, false, false, false,
|
||||
0, CallingConv::C, false, /*isReturnValueUsed=*/false,
|
||||
0, CallingConv::C, /*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/false,
|
||||
DAG.getExternalSymbol(bzeroEntry, IntPtr), Args,
|
||||
DAG, dl);
|
||||
return CallResult.second;
|
||||
|
@ -487,8 +487,8 @@ LowerLOAD(SDValue Op, SelectionDAG &DAG) const {
|
||||
|
||||
std::pair<SDValue, SDValue> CallResult =
|
||||
LowerCallTo(Chain, IntPtrTy, false, false,
|
||||
false, false, 0, CallingConv::C, false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
false, false, 0, CallingConv::C, /*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("__misaligned_load", getPointerTy()),
|
||||
Args, DAG, DL);
|
||||
|
||||
@ -549,8 +549,8 @@ LowerSTORE(SDValue Op, SelectionDAG &DAG) const
|
||||
|
||||
std::pair<SDValue, SDValue> CallResult =
|
||||
LowerCallTo(Chain, Type::getVoidTy(*DAG.getContext()), false, false,
|
||||
false, false, 0, CallingConv::C, false,
|
||||
/*isReturnValueUsed=*/true,
|
||||
false, false, 0, CallingConv::C, /*isTailCall=*/false,
|
||||
/*doesNotRet=*/false, /*isReturnValueUsed=*/true,
|
||||
DAG.getExternalSymbol("__misaligned_store", getPointerTy()),
|
||||
Args, DAG, dl);
|
||||
|
||||
@ -875,7 +875,7 @@ LowerINIT_TRAMPOLINE(SDValue Op, SelectionDAG &DAG) const {
|
||||
SDValue
|
||||
XCoreTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
@ -175,9 +175,8 @@ namespace llvm {
|
||||
SmallVectorImpl<SDValue> &InVals) const;
|
||||
|
||||
virtual SDValue
|
||||
LowerCall(SDValue Chain, SDValue Callee,
|
||||
CallingConv::ID CallConv, bool isVarArg,
|
||||
bool &isTailCall,
|
||||
LowerCall(SDValue Chain, SDValue Callee, CallingConv::ID CallConv,
|
||||
bool isVarArg, bool doesNotRet, bool &isTailCall,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
|
30
test/CodeGen/ARM/call-noret.ll
Normal file
30
test/CodeGen/ARM/call-noret.ll
Normal file
@ -0,0 +1,30 @@
|
||||
; RUN: llc < %s -mtriple=armv7-apple-ios -mcpu=cortex-a8 | FileCheck %s -check-prefix=ARM
|
||||
; RUN: llc < %s -mtriple=thumbv7-apple-ios -mcpu=cortex-a8 | FileCheck %s -check-prefix=T2
|
||||
; rdar://8979299
|
||||
|
||||
define void @t1() noreturn nounwind ssp {
|
||||
entry:
|
||||
; ARM: t1:
|
||||
; ARM: mov lr, pc
|
||||
; ARM: b _bar
|
||||
|
||||
; T2: t1:
|
||||
; T2: blx _bar
|
||||
tail call void @bar() noreturn nounwind
|
||||
unreachable
|
||||
}
|
||||
|
||||
define void @t2() noreturn nounwind ssp {
|
||||
entry:
|
||||
; ARM: t2:
|
||||
; ARM: mov lr, pc
|
||||
; ARM: b _t1
|
||||
|
||||
; T2: t2:
|
||||
; T2: mov lr, pc
|
||||
; T2: b.w _t1
|
||||
tail call void @t1() noreturn nounwind
|
||||
unreachable
|
||||
}
|
||||
|
||||
declare void @bar() noreturn
|
Loading…
Reference in New Issue
Block a user