mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-23 19:59:57 +00:00
Fix __builtin_setjmp in combination with sjlj exception handling.
llvm.eh.sjlj.setjmp was used as part of the SjLj exception handling style but is also used in clang to implement __builtin_setjmp. The ARM backend needs to output additional dispatch tables for the SjLj exception handling style, these tables however can't be emitted if llvm.eh.sjlj.setjmp is simply used for __builtin_setjmp and no actual landing pad blocks exist. To solve this issue a new llvm.eh.sjlj.setup_dispatch intrinsic is introduced which is used instead of llvm.eh.sjlj.setjmp in the SjLj exception handling lowering, so we can differentiate between the case where we actually need to setup a dispatch table and the case where we just need the __builtin_setjmp semantic. Differential Revision: http://reviews.llvm.org/D9313 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@242481 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
ec56faaa64
commit
9e4654db1a
@ -108,6 +108,10 @@ namespace ISD {
|
||||
/// and returns an outchain.
|
||||
EH_SJLJ_LONGJMP,
|
||||
|
||||
/// OUTCHAIN = EH_SJLJ_SETUP_DISPATCH(INCHAIN)
|
||||
/// The target initializes the dispatch table here.
|
||||
EH_SJLJ_SETUP_DISPATCH,
|
||||
|
||||
/// TargetConstant* - Like Constant*, but the DAG does not do any folding,
|
||||
/// simplification, or lowering of the constant. They are used for constants
|
||||
/// which are known to fit in the immediate fields of their users, or for
|
||||
|
@ -455,6 +455,7 @@ let Properties = [IntrNoMem] in {
|
||||
def int_eh_sjlj_functioncontext : Intrinsic<[], [llvm_ptr_ty]>;
|
||||
def int_eh_sjlj_setjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty]>;
|
||||
def int_eh_sjlj_longjmp : Intrinsic<[], [llvm_ptr_ty], [IntrNoReturn]>;
|
||||
def int_eh_sjlj_setup_dispatch : Intrinsic<[], []>;
|
||||
|
||||
//===---------------- Generic Variable Attribute Intrinsics----------------===//
|
||||
//
|
||||
|
@ -1264,6 +1264,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
|
||||
case ISD::FRAME_TO_ARGS_OFFSET:
|
||||
case ISD::EH_SJLJ_SETJMP:
|
||||
case ISD::EH_SJLJ_LONGJMP:
|
||||
case ISD::EH_SJLJ_SETUP_DISPATCH:
|
||||
// These operations lie about being legal: when they claim to be legal,
|
||||
// they should actually be expanded.
|
||||
Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
|
||||
|
@ -4410,6 +4410,11 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||
getRoot(), getValue(I.getArgOperand(0))));
|
||||
return nullptr;
|
||||
}
|
||||
case Intrinsic::eh_sjlj_setup_dispatch: {
|
||||
DAG.setRoot(DAG.getNode(ISD::EH_SJLJ_SETUP_DISPATCH, sdl, MVT::Other,
|
||||
getRoot()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case Intrinsic::masked_gather:
|
||||
visitMaskedGather(I);
|
||||
|
@ -102,6 +102,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
|
||||
case ISD::EH_RETURN: return "EH_RETURN";
|
||||
case ISD::EH_SJLJ_SETJMP: return "EH_SJLJ_SETJMP";
|
||||
case ISD::EH_SJLJ_LONGJMP: return "EH_SJLJ_LONGJMP";
|
||||
case ISD::EH_SJLJ_SETUP_DISPATCH: return "EH_SJLJ_SETUP_DISPATCH";
|
||||
case ISD::ConstantPool: return "ConstantPool";
|
||||
case ISD::TargetIndex: return "TargetIndex";
|
||||
case ISD::ExternalSymbol: return "ExternalSymbol";
|
||||
|
@ -50,7 +50,7 @@ class SjLjEHPrepare : public FunctionPass {
|
||||
Type *FunctionContextTy;
|
||||
Constant *RegisterFn;
|
||||
Constant *UnregisterFn;
|
||||
Constant *BuiltinSetjmpFn;
|
||||
Constant *BuiltinSetupDispatchFn;
|
||||
Constant *FrameAddrFn;
|
||||
Constant *StackAddrFn;
|
||||
Constant *StackRestoreFn;
|
||||
@ -112,7 +112,8 @@ bool SjLjEHPrepare::doInitialization(Module &M) {
|
||||
FrameAddrFn = Intrinsic::getDeclaration(&M, Intrinsic::frameaddress);
|
||||
StackAddrFn = Intrinsic::getDeclaration(&M, Intrinsic::stacksave);
|
||||
StackRestoreFn = Intrinsic::getDeclaration(&M, Intrinsic::stackrestore);
|
||||
BuiltinSetjmpFn = Intrinsic::getDeclaration(&M, Intrinsic::eh_sjlj_setjmp);
|
||||
BuiltinSetupDispatchFn =
|
||||
Intrinsic::getDeclaration(&M, Intrinsic::eh_sjlj_setup_dispatch);
|
||||
LSDAAddrFn = Intrinsic::getDeclaration(&M, Intrinsic::eh_sjlj_lsda);
|
||||
CallSiteFn = Intrinsic::getDeclaration(&M, Intrinsic::eh_sjlj_callsite);
|
||||
FuncCtxFn = Intrinsic::getDeclaration(&M, Intrinsic::eh_sjlj_functioncontext);
|
||||
@ -421,9 +422,8 @@ bool SjLjEHPrepare::setupEntryBlockAndCallSites(Function &F) {
|
||||
Val = Builder.CreateCall(StackAddrFn, {}, "sp");
|
||||
Builder.CreateStore(Val, StackPtr, /*isVolatile=*/true);
|
||||
|
||||
// Call the setjmp instrinsic. It fills in the rest of the jmpbuf.
|
||||
Value *SetjmpArg = Builder.CreateBitCast(JBufPtr, Builder.getInt8PtrTy());
|
||||
Builder.CreateCall(BuiltinSetjmpFn, SetjmpArg);
|
||||
// Call the setup_dispatch instrinsic. It fills in the rest of the jmpbuf.
|
||||
Builder.CreateCall(BuiltinSetupDispatchFn, {});
|
||||
|
||||
// Store a pointer to the function context so that the back-end will know
|
||||
// where to look for it.
|
||||
|
@ -849,11 +849,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
|
||||
|
||||
// We want to custom lower some of our intrinsics.
|
||||
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
|
||||
if (Subtarget->isTargetDarwin()) {
|
||||
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom);
|
||||
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom);
|
||||
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom);
|
||||
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom);
|
||||
setOperationAction(ISD::EH_SJLJ_SETUP_DISPATCH, MVT::Other, Custom);
|
||||
if (Subtarget->isTargetDarwin())
|
||||
setLibcallName(RTLIB::UNWIND_RESUME, "_Unwind_SjLj_Resume");
|
||||
}
|
||||
|
||||
setOperationAction(ISD::SETCC, MVT::i32, Expand);
|
||||
setOperationAction(ISD::SETCC, MVT::f32, Expand);
|
||||
@ -1069,7 +1069,8 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||
case ARMISD::VMOVDRR: return "ARMISD::VMOVDRR";
|
||||
|
||||
case ARMISD::EH_SJLJ_SETJMP: return "ARMISD::EH_SJLJ_SETJMP";
|
||||
case ARMISD::EH_SJLJ_LONGJMP:return "ARMISD::EH_SJLJ_LONGJMP";
|
||||
case ARMISD::EH_SJLJ_LONGJMP: return "ARMISD::EH_SJLJ_LONGJMP";
|
||||
case ARMISD::EH_SJLJ_SETUP_DISPATCH: return "ARMISD::EH_SJLJ_SETUP_DISPATCH";
|
||||
|
||||
case ARMISD::TC_RETURN: return "ARMISD::TC_RETURN";
|
||||
|
||||
@ -2722,6 +2723,13 @@ ARMTargetLowering::LowerEH_SJLJ_LONGJMP(SDValue Op, SelectionDAG &DAG) const {
|
||||
Op.getOperand(1), DAG.getConstant(0, dl, MVT::i32));
|
||||
}
|
||||
|
||||
SDValue ARMTargetLowering::LowerEH_SJLJ_SETUP_DISPATCH(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
SDLoc dl(Op);
|
||||
return DAG.getNode(ARMISD::EH_SJLJ_SETUP_DISPATCH, dl, MVT::Other,
|
||||
Op.getOperand(0));
|
||||
}
|
||||
|
||||
SDValue
|
||||
ARMTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG,
|
||||
const ARMSubtarget *Subtarget) const {
|
||||
@ -6573,6 +6581,7 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
||||
case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG);
|
||||
case ISD::EH_SJLJ_SETJMP: return LowerEH_SJLJ_SETJMP(Op, DAG);
|
||||
case ISD::EH_SJLJ_LONGJMP: return LowerEH_SJLJ_LONGJMP(Op, DAG);
|
||||
case ISD::EH_SJLJ_SETUP_DISPATCH: return LowerEH_SJLJ_SETUP_DISPATCH(Op, DAG);
|
||||
case ISD::INTRINSIC_WO_CHAIN: return LowerINTRINSIC_WO_CHAIN(Op, DAG,
|
||||
Subtarget);
|
||||
case ISD::BITCAST: return ExpandBITCAST(Op.getNode(), DAG);
|
||||
@ -7735,6 +7744,9 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI,
|
||||
case ARM::tInt_eh_sjlj_setjmp:
|
||||
case ARM::t2Int_eh_sjlj_setjmp:
|
||||
case ARM::t2Int_eh_sjlj_setjmp_nofp:
|
||||
return BB;
|
||||
|
||||
case ARM::Int_eh_sjlj_setup_dispatch:
|
||||
EmitSjLjDispatchBlock(MI, BB);
|
||||
return BB;
|
||||
|
||||
|
@ -79,6 +79,7 @@ namespace llvm {
|
||||
|
||||
EH_SJLJ_SETJMP, // SjLj exception handling setjmp.
|
||||
EH_SJLJ_LONGJMP, // SjLj exception handling longjmp.
|
||||
EH_SJLJ_SETUP_DISPATCH, // SjLj exception handling setup_dispatch.
|
||||
|
||||
TC_RETURN, // Tail call return pseudo.
|
||||
|
||||
@ -496,6 +497,7 @@ namespace llvm {
|
||||
ISD::ArgFlagsTy Flags) const;
|
||||
SDValue LowerEH_SJLJ_SETJMP(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerEH_SJLJ_LONGJMP(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerEH_SJLJ_SETUP_DISPATCH(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG,
|
||||
const ARMSubtarget *Subtarget) const;
|
||||
SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
|
@ -59,6 +59,7 @@ def SDT_ARMThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>;
|
||||
def SDT_ARMEH_SJLJ_Setjmp : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisPtrTy<1>,
|
||||
SDTCisInt<2>]>;
|
||||
def SDT_ARMEH_SJLJ_Longjmp: SDTypeProfile<0, 2, [SDTCisPtrTy<0>, SDTCisInt<1>]>;
|
||||
def SDT_ARMEH_SJLJ_SetupDispatch: SDTypeProfile<0, 0, []>;
|
||||
|
||||
def SDT_ARMMEMBARRIER : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
|
||||
|
||||
@ -163,6 +164,9 @@ def ARMeh_sjlj_setjmp: SDNode<"ARMISD::EH_SJLJ_SETJMP",
|
||||
def ARMeh_sjlj_longjmp: SDNode<"ARMISD::EH_SJLJ_LONGJMP",
|
||||
SDT_ARMEH_SJLJ_Longjmp,
|
||||
[SDNPHasChain, SDNPSideEffect]>;
|
||||
def ARMeh_sjlj_setup_dispatch: SDNode<"ARMISD::EH_SJLJ_SETUP_DISPATCH",
|
||||
SDT_ARMEH_SJLJ_SetupDispatch,
|
||||
[SDNPHasChain, SDNPSideEffect]>;
|
||||
|
||||
def ARMMemBarrierMCR : SDNode<"ARMISD::MEMBARRIER_MCR", SDT_ARMMEMBARRIER,
|
||||
[SDNPHasChain, SDNPSideEffect]>;
|
||||
@ -5301,6 +5305,10 @@ def Int_eh_sjlj_longjmp : PseudoInst<(outs), (ins GPR:$src, GPR:$scratch),
|
||||
Requires<[IsARM]>;
|
||||
}
|
||||
|
||||
let isBarrier = 1, hasSideEffects = 1, usesCustomInserter = 1 in
|
||||
def Int_eh_sjlj_setup_dispatch : PseudoInst<(outs), (ins), NoItinerary,
|
||||
[(ARMeh_sjlj_setup_dispatch)]>;
|
||||
|
||||
// eh.sjlj.dispatchsetup pseudo-instruction.
|
||||
// This pseudo is used for both ARM and Thumb. Any differences are handled when
|
||||
// the pseudo is expanded (which happens before any passes that need the
|
||||
|
113
test/CodeGen/ARM/setjmp_longjmp.ll
Normal file
113
test/CodeGen/ARM/setjmp_longjmp.ll
Normal file
@ -0,0 +1,113 @@
|
||||
; RUN: llc %s -o - | FileCheck %s
|
||||
target triple = "armv7-apple-ios"
|
||||
|
||||
declare i32 @llvm.eh.sjlj.setjmp(i8*)
|
||||
declare void @llvm.eh.sjlj.longjmp(i8*)
|
||||
@g = external global i32
|
||||
|
||||
declare void @may_throw()
|
||||
declare i32 @__gxx_personality_sj0(...)
|
||||
declare i8* @__cxa_begin_catch(i8*)
|
||||
declare void @__cxa_end_catch()
|
||||
declare i32 @llvm.eh.typeid.for(i8*)
|
||||
declare i8* @llvm.frameaddress(i32)
|
||||
declare i8* @llvm.stacksave()
|
||||
@_ZTIPKc = external constant i8*
|
||||
|
||||
; CHECK-LABEL: foobar
|
||||
;
|
||||
; setjmp sequence:
|
||||
; CHECK: add [[PCREG:r[0-9]+]], pc, #8
|
||||
; CHECK-NEXT: str [[PCREG]], {{\[}}[[BUFREG:r[0-9]+]], #4]
|
||||
; CHECK-NEXT: mov r0, #0
|
||||
; CHECK-NEXT: add pc, pc, #0
|
||||
; CHECK-NEXT: mov r0, #1
|
||||
;
|
||||
; longjmp sequence:
|
||||
; CHECK: ldr sp, [{{\s*}}[[BUFREG:r[0-9]+]], #8]
|
||||
; CHECK-NEXT: ldr [[DESTREG:r[0-9]+]], {{\[}}[[BUFREG]], #4]
|
||||
; CHECK-NEXT: ldr r7, {{\[}}[[BUFREG]]{{\]}}
|
||||
; CHECK-NEXT: bx [[DESTREG]]
|
||||
define void @foobar() {
|
||||
entry:
|
||||
%buf = alloca [5 x i8*], align 4
|
||||
%arraydecay = getelementptr inbounds [5 x i8*], [5 x i8*]* %buf, i32 0, i32 0
|
||||
%bufptr = bitcast i8** %arraydecay to i8*
|
||||
; Note: This is simplified, in reality you have to store the framepointer +
|
||||
; stackpointer in the buffer as well for this to be legal!
|
||||
%setjmpres = call i32 @llvm.eh.sjlj.setjmp(i8* %bufptr)
|
||||
%tobool = icmp ne i32 %setjmpres, 0
|
||||
br i1 %tobool, label %if.then, label %if.else
|
||||
|
||||
if.then:
|
||||
store volatile i32 1, i32* @g, align 4
|
||||
br label %if.end
|
||||
|
||||
if.else:
|
||||
store volatile i32 0, i32* @g, align 4
|
||||
call void @llvm.eh.sjlj.longjmp(i8* %bufptr)
|
||||
unreachable
|
||||
|
||||
if.end:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: combine_sjlj_eh_and_setjmp_longjmp
|
||||
; Check that we can mix sjlj exception handling with __builtin_setjmp
|
||||
; and __builtin_longjmp.
|
||||
;
|
||||
; setjmp sequence:
|
||||
; CHECK: add [[PCREG:r[0-9]+]], pc, #8
|
||||
; CHECK-NEXT: str [[PCREG]], {{\[}}[[BUFREG:r[0-9]+]], #4]
|
||||
; CHECK-NEXT: mov r0, #0
|
||||
; CHECK-NEXT: add pc, pc, #0
|
||||
; CHECK-NEXT: mov r0, #1
|
||||
;
|
||||
; longjmp sequence:
|
||||
; CHECK: ldr sp, [{{\s*}}[[BUFREG:r[0-9]+]], #8]
|
||||
; CHECK-NEXT: ldr [[DESTREG:r[0-9]+]], {{\[}}[[BUFREG]], #4]
|
||||
; CHECK-NEXT: ldr r7, {{\[}}[[BUFREG]]{{\]}}
|
||||
; CHECK-NEXT: bx [[DESTREG]]
|
||||
define void @combine_sjlj_eh_and_setjmp_longjmp() personality i8* bitcast (i32 (...)* @__gxx_personality_sj0 to i8*) {
|
||||
entry:
|
||||
%buf = alloca [5 x i8*], align 4
|
||||
invoke void @may_throw() to label %try.cont unwind label %lpad
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 } catch i8* bitcast (i8** @_ZTIPKc to i8*)
|
||||
%1 = extractvalue { i8*, i32 } %0, 1
|
||||
%2 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIPKc to i8*)) #3
|
||||
%matches = icmp eq i32 %1, %2
|
||||
br i1 %matches, label %catch, label %eh.resume
|
||||
|
||||
catch:
|
||||
%3 = extractvalue { i8*, i32 } %0, 0
|
||||
%4 = tail call i8* @__cxa_begin_catch(i8* %3) #3
|
||||
store volatile i32 0, i32* @g, align 4
|
||||
%5 = bitcast [5 x i8*]* %buf to i8*
|
||||
%arraydecay = getelementptr inbounds [5 x i8*], [5 x i8*]* %buf, i64 0, i64 0
|
||||
%6 = tail call i8* @llvm.frameaddress(i32 0)
|
||||
store i8* %6, i8** %arraydecay, align 16
|
||||
%7 = tail call i8* @llvm.stacksave()
|
||||
%8 = getelementptr [5 x i8*], [5 x i8*]* %buf, i64 0, i64 2
|
||||
store i8* %7, i8** %8, align 16
|
||||
%9 = call i32 @llvm.eh.sjlj.setjmp(i8* %5)
|
||||
%tobool = icmp eq i32 %9, 0
|
||||
br i1 %tobool, label %if.else, label %if.then
|
||||
|
||||
if.then:
|
||||
store volatile i32 2, i32* @g, align 4
|
||||
call void @__cxa_end_catch() #3
|
||||
br label %try.cont
|
||||
|
||||
if.else:
|
||||
store volatile i32 1, i32* @g, align 4
|
||||
call void @llvm.eh.sjlj.longjmp(i8* %5)
|
||||
unreachable
|
||||
|
||||
eh.resume:
|
||||
resume { i8*, i32 } %0
|
||||
|
||||
try.cont:
|
||||
ret void
|
||||
}
|
Loading…
Reference in New Issue
Block a user