From 0e0da734bbdfa1d3f55cd04db31d83b97e4556f7 Mon Sep 17 00:00:00 2001 From: Jim Grosbach Date: Tue, 12 May 2009 23:59:14 +0000 Subject: [PATCH] Add support for GCC compatible builtin setjmp and longjmp intrinsics. This is a supporting preliminary patch for GCC-compatible SjLJ exception handling. Note that these intrinsics are not designed to be invoked directly by the user, but rather used by the front-end as target hooks for exception handling. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@71610 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CodeGen/MachineFunction.h | 12 +++++++++ include/llvm/Intrinsics.td | 5 ++++ lib/CodeGen/MachineFunction.cpp | 1 + lib/CodeGen/PrologEpilogInserter.cpp | 2 +- .../SelectionDAG/SelectionDAGBuild.cpp | 6 +++++ lib/Target/ARM/ARMISelLowering.cpp | 27 ++++++++++++++++--- lib/Target/ARM/ARMISelLowering.h | 5 ++++ lib/Target/ARM/ARMInstrInfo.cpp | 1 + lib/Target/ARM/ARMInstrInfo.td | 23 ++++++++++++++++ 9 files changed, 77 insertions(+), 5 deletions(-) diff --git a/include/llvm/CodeGen/MachineFunction.h b/include/llvm/CodeGen/MachineFunction.h index a110e5846ac..469ae026f01 100644 --- a/include/llvm/CodeGen/MachineFunction.h +++ b/include/llvm/CodeGen/MachineFunction.h @@ -70,6 +70,10 @@ class MachineFunction : private Annotation { const Function *Fn; const TargetMachine &Target; + // HasBuiltinSetjmp - true if the function uses builtin_setjmp. Used to + // adjust callee-saved register tracking. + bool HasBuiltinSetjmp; + // RegInfo - Information about each register in use in the function. MachineRegisterInfo *RegInfo; @@ -123,6 +127,14 @@ public: /// const TargetMachine &getTarget() const { return Target; } + /// doesHaveBuiltinSetjmp - Return whether this function uses builtin_setjmp + /// + bool doesHaveBuiltinSetjmp() const { return HasBuiltinSetjmp; } + + /// setHasBuiltinSetjmp - Mark whether this function uses builtin_setjmp + /// + void setHasBuiltinSetjmp (bool flag) { HasBuiltinSetjmp = flag; } + /// getRegInfo - Return information about the registers currently in use. /// MachineRegisterInfo &getRegInfo() { return *RegInfo; } diff --git a/include/llvm/Intrinsics.td b/include/llvm/Intrinsics.td index 6e4ce82c4c0..7c869f0aa30 100644 --- a/include/llvm/Intrinsics.td +++ b/include/llvm/Intrinsics.td @@ -299,6 +299,11 @@ def int_eh_unwind_init: Intrinsic<[llvm_void_ty]>, def int_eh_dwarf_cfa : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty]>; +let Properties = [IntrNoMem] in { +def int_builtinsetjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty]>; +def int_builtinlongjmp : Intrinsic<[llvm_void_ty], [llvm_ptr_ty, llvm_i32_ty]>; +} + //===---------------- Generic Variable Attribute Intrinsics----------------===// // def int_var_annotation : Intrinsic<[llvm_void_ty], diff --git a/lib/CodeGen/MachineFunction.cpp b/lib/CodeGen/MachineFunction.cpp index 5135308e980..cb19bec0896 100644 --- a/lib/CodeGen/MachineFunction.cpp +++ b/lib/CodeGen/MachineFunction.cpp @@ -121,6 +121,7 @@ MachineFunction::MachineFunction(const Function *F, MachineRegisterInfo(*TM.getRegisterInfo()); else RegInfo = 0; + HasBuiltinSetjmp = false; MFInfo = 0; FrameInfo = new (Allocator.Allocate()) MachineFrameInfo(*TM.getFrameInfo()); diff --git a/lib/CodeGen/PrologEpilogInserter.cpp b/lib/CodeGen/PrologEpilogInserter.cpp index 6f95d1c3cfe..d9665ccc15e 100644 --- a/lib/CodeGen/PrologEpilogInserter.cpp +++ b/lib/CodeGen/PrologEpilogInserter.cpp @@ -180,7 +180,7 @@ void PEI::calculateCalleeSavedRegisters(MachineFunction &Fn) { std::vector CSI; for (unsigned i = 0; CSRegs[i]; ++i) { unsigned Reg = CSRegs[i]; - if (Fn.getRegInfo().isPhysRegUsed(Reg)) { + if (Fn.getRegInfo().isPhysRegUsed(Reg) || Fn.doesHaveBuiltinSetjmp()) { // If the reg is modified, save it! CSI.push_back(CalleeSavedInfo(Reg, CSRegClasses[i])); } else { diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp index b340d0c971b..cecd529707c 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuild.cpp @@ -3849,6 +3849,12 @@ SelectionDAGLowering::visitIntrinsicCall(CallInst &I, unsigned Intrinsic) { case Intrinsic::longjmp: return "_longjmp"+!TLI.usesUnderscoreLongJmp(); break; + case Intrinsic::builtinsetjmp: + // Mark this function has using builtin_setjmp so context gets preserved + DAG.getMachineFunction().setHasBuiltinSetjmp(true); + // Turn it into a target intrinsic node for the codegen + visitTargetIntrinsic(I, Intrinsic); + return 0; case Intrinsic::memcpy: { SDValue Op1 = getValue(I.getOperand(1)); SDValue Op2 = getValue(I.getOperand(2)); diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp index 7704209190f..48e197d04f6 100644 --- a/lib/Target/ARM/ARMISelLowering.cpp +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -1035,14 +1035,19 @@ SDValue ARMTargetLowering::LowerGLOBAL_OFFSET_TABLE(SDValue Op, return DAG.getNode(ARMISD::PIC_ADD, dl, PtrVT, Result, PICLabel); } -static SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) { +SDValue +ARMTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) { MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(); unsigned IntNo = cast(Op.getOperand(0))->getZExtValue(); + DebugLoc dl = Op.getDebugLoc(); switch (IntNo) { default: return SDValue(); // Don't custom lower most intrinsics. case Intrinsic::arm_thread_pointer: - return DAG.getNode(ARMISD::THREAD_POINTER, DebugLoc::getUnknownLoc(), - PtrVT); + return DAG.getNode(ARMISD::THREAD_POINTER, dl, PtrVT); + case Intrinsic::builtinsetjmp: + SDValue Res = DAG.getNode(ARMISD::BUILTIN_SETJMP, dl, MVT::i32, + Op.getOperand(1)); + return Res; } } @@ -1431,6 +1436,20 @@ static SDValue LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) { return DAG.getNode(ARMISD::CNEG, dl, VT, AbsVal, AbsVal, ARMCC, CCR, Cmp); } +SDValue ARMTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) { + MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo(); + MFI->setFrameAddressIsTaken(true); + MVT VT = Op.getValueType(); + DebugLoc dl = Op.getDebugLoc(); // FIXME probably not meaningful + unsigned Depth = cast(Op.getOperand(0))->getZExtValue(); + unsigned FrameReg = (Subtarget->isThumb() || Subtarget->useThumbBacktraces()) + ? ARM::R7 : ARM::R11; + SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), dl, FrameReg, VT); + while (Depth--) + FrameAddr = DAG.getLoad(VT, dl, DAG.getEntryNode(), FrameAddr, NULL, 0); + return FrameAddr; +} + SDValue ARMTargetLowering::EmitTargetCodeForMemcpy(SelectionDAG &DAG, DebugLoc dl, SDValue Chain, @@ -1612,7 +1631,7 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) { case ISD::FCOPYSIGN: return LowerFCOPYSIGN(Op, DAG); case ISD::FORMAL_ARGUMENTS: return LowerFORMAL_ARGUMENTS(Op, DAG); case ISD::RETURNADDR: break; - case ISD::FRAMEADDR: break; + case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG); case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG); case ISD::INTRINSIC_WO_CHAIN: return LowerINTRINSIC_WO_CHAIN(Op, DAG); case ISD::BIT_CONVERT: return ExpandBIT_CONVERT(Op.getNode(), DAG); diff --git a/lib/Target/ARM/ARMISelLowering.h b/lib/Target/ARM/ARMISelLowering.h index 4c49350c327..b0f9e27d88c 100644 --- a/lib/Target/ARM/ARMISelLowering.h +++ b/lib/Target/ARM/ARMISelLowering.h @@ -64,6 +64,9 @@ namespace llvm { FMRRD, // double to two gprs. FMDRR, // Two gprs to double. + BUILTIN_SETJMP, // exception handling setjmp + BUILTIN_LONGJMP, // exception handling longjmp + THREAD_POINTER }; } @@ -154,6 +157,7 @@ namespace llvm { SDNode *LowerCallResult(SDValue Chain, SDValue InFlag, CallSDNode *TheCall, unsigned CallingConv, SelectionDAG &DAG); SDValue LowerCALL(SDValue Op, SelectionDAG &DAG); + SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG); SDValue LowerRET(SDValue Op, SelectionDAG &DAG); SDValue LowerGlobalAddressDarwin(SDValue Op, SelectionDAG &DAG); SDValue LowerGlobalAddressELF(SDValue Op, SelectionDAG &DAG); @@ -165,6 +169,7 @@ namespace llvm { SDValue LowerGLOBAL_OFFSET_TABLE(SDValue Op, SelectionDAG &DAG); SDValue LowerFORMAL_ARGUMENTS(SDValue Op, SelectionDAG &DAG); SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG); + SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG); SDValue EmitTargetCodeForMemcpy(SelectionDAG &DAG, DebugLoc dl, SDValue Chain, diff --git a/lib/Target/ARM/ARMInstrInfo.cpp b/lib/Target/ARM/ARMInstrInfo.cpp index f186823f0c8..235d1d1d50d 100644 --- a/lib/Target/ARM/ARMInstrInfo.cpp +++ b/lib/Target/ARM/ARMInstrInfo.cpp @@ -991,6 +991,7 @@ unsigned ARMInstrInfo::GetInstSizeInBytes(const MachineInstr *MI) const { // If this machine instr is a constant pool entry, its size is recorded as // operand #2. return MI->getOperand(2).getImm(); + case ARM::Int_builtin_setjmp: return 12; case ARM::BR_JTr: case ARM::BR_JTm: case ARM::BR_JTadd: diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index 62f740be86e..cdc0ee55d77 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -40,6 +40,7 @@ def SDT_ARMPICAdd : SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, SDTCisPtrTy<1>, SDTCisVT<2, i32>]>; def SDT_ARMThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>; +def SDT_ARMBuiltinSetjmp : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisPtrTy<1>]>; // Node definitions. def ARMWrapper : SDNode<"ARMISD::Wrapper", SDTIntUnaryOp>; @@ -84,6 +85,7 @@ def ARMsra_flag : SDNode<"ARMISD::SRA_FLAG", SDTIntUnaryOp, [SDNPOutFlag]>; def ARMrrx : SDNode<"ARMISD::RRX" , SDTIntUnaryOp, [SDNPInFlag ]>; def ARMthread_pointer: SDNode<"ARMISD::THREAD_POINTER", SDT_ARMThreadPointer>; +def ARMbuiltin_setjmp: SDNode<"ARMISD::BUILTIN_SETJMP", SDT_ARMBuiltinSetjmp>; //===----------------------------------------------------------------------===// // ARM Instruction Predicate Definitions. @@ -1265,6 +1267,27 @@ let isCall = 1, [(set R0, ARMthread_pointer)]>; } +//===----------------------------------------------------------------------===// +// SJLJ Exception handling intrinsics +// setjmp() is a three instruction sequence to store the return address +// and save #0 in R0 for the non-longjmp case. +// Since by its nature we may be coming from some other function to get +// here, and we're using the stack frame for the containing function to +// save/restore registers, we can't keep anything live in regs across +// the setjmp(), else it will almost certainly have been tromped upon +// when we get here from a longjmp(). We force everthing out of registers +// except for our own input by listing the relevant registers in Defs. +let Defs = + [ R0, R1, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR, + D0, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15 ] in { + def Int_builtin_setjmp : XI<(outs), (ins GPR:$src), + AddrModeNone, SizeSpecial, IndexModeNone, Pseudo, + "add r0, pc, #4\n\t" + "str r0, [$src, #+4]\n\t" + "mov r0, #0 @ setjmp", "", + [(set R0, (ARMbuiltin_setjmp GPR:$src))]>; +} + //===----------------------------------------------------------------------===// // Non-Instruction Patterns //