From 0e0da734bbdfa1d3f55cd04db31d83b97e4556f7 Mon Sep 17 00:00:00 2001
From: Jim Grosbach <grosbach@apple.com>
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>())
                   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<CalleeSavedInfo> 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<ConstantSDNode>(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<ConstantSDNode>(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
 //