XCore target: Lower EH_RETURN
authorRobert Lytton <robert@xmos.com>
Mon, 6 Jan 2014 14:21:07 +0000 (14:21 +0000)
committerRobert Lytton <robert@xmos.com>
Mon, 6 Jan 2014 14:21:07 +0000 (14:21 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@198615 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/XCore/XCoreFrameLowering.cpp
lib/Target/XCore/XCoreISelLowering.cpp
lib/Target/XCore/XCoreISelLowering.h
lib/Target/XCore/XCoreInstrInfo.td
test/CodeGen/XCore/llvm-intrinsics.ll

index 3d1fb9cafc13d6726ab5c25585de6ecc75688fd8..109b74d445c52bcca78ded0da98177abe1547ee7 100644 (file)
@@ -252,6 +252,15 @@ void XCoreFrameLowering::emitEpilogue(MachineFunction &MF,
   DebugLoc dl = MBBI->getDebugLoc();
   unsigned RetOpcode = MBBI->getOpcode();
 
+  if (RetOpcode == XCore::EH_RETURN) {
+    unsigned EhStackReg = MBBI->getOperand(0).getReg();
+    unsigned EhHandlerReg = MBBI->getOperand(1).getReg();
+    BuildMI(MBB, MBBI, dl, TII.get(XCore::SETSP_1r)).addReg(EhStackReg);
+    BuildMI(MBB, MBBI, dl, TII.get(XCore::BAU_1r)).addReg(EhHandlerReg);
+    MBB.erase(MBBI);  // Erase the previous return instruction.
+    return;
+  }
+
   // Work out frame sizes.
   // We will adjust the SP in stages towards the final FrameSize.
   int RemainingAdj = MFI->getStackSize();
index c0edee55e43b263adbcd190bc90cc8e31d2d3af4..dd268c4cddce1151a956b97d908ba67c71e6ba1f 100644 (file)
@@ -61,6 +61,7 @@ getTargetNodeName(unsigned Opcode) const
     case XCoreISD::BR_JT             : return "XCoreISD::BR_JT";
     case XCoreISD::BR_JT32           : return "XCoreISD::BR_JT32";
     case XCoreISD::FRAME_TO_ARGS_OFFSET : return "XCoreISD::FRAME_TO_ARGS_OFFSET";
+    case XCoreISD::EH_RETURN         : return "XCoreISD::EH_RETURN";
     case XCoreISD::MEMBARRIER        : return "XCoreISD::MEMBARRIER";
     default                          : return NULL;
   }
@@ -152,6 +153,7 @@ XCoreTargetLowering::XCoreTargetLowering(XCoreTargetMachine &XTM)
   setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand);
 
   // Exception handling
+  setOperationAction(ISD::EH_RETURN, MVT::Other, Custom);
   setExceptionPointerRegister(XCore::R0);
   setExceptionSelectorRegister(XCore::R1);
   setOperationAction(ISD::FRAME_TO_ARGS_OFFSET, MVT::i32, Custom);
@@ -199,6 +201,7 @@ SDValue XCoreTargetLowering::
 LowerOperation(SDValue Op, SelectionDAG &DAG) const {
   switch (Op.getOpcode())
   {
+  case ISD::EH_RETURN:          return LowerEH_RETURN(Op, DAG);
   case ISD::GlobalAddress:      return LowerGlobalAddress(Op, DAG);
   case ISD::BlockAddress:       return LowerBlockAddress(Op, DAG);
   case ISD::ConstantPool:       return LowerConstantPool(Op, DAG);
@@ -838,6 +841,45 @@ LowerFRAME_TO_ARGS_OFFSET(SDValue Op, SelectionDAG &DAG) const {
   return DAG.getNode(XCoreISD::FRAME_TO_ARGS_OFFSET, SDLoc(Op), MVT::i32);
 }
 
+SDValue XCoreTargetLowering::
+LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const {
+  // OUTCHAIN = EH_RETURN(INCHAIN, OFFSET, HANDLER)
+  // This node represents 'eh_return' gcc dwarf builtin, which is used to
+  // return from exception. The general meaning is: adjust stack by OFFSET and
+  // pass execution to HANDLER.
+  MachineFunction &MF = DAG.getMachineFunction();
+  SDValue Chain     = Op.getOperand(0);
+  SDValue Offset    = Op.getOperand(1);
+  SDValue Handler   = Op.getOperand(2);
+  SDLoc dl(Op);
+
+  // Absolute SP = (FP + FrameToArgs) + Offset
+  const TargetRegisterInfo *RegInfo = getTargetMachine().getRegisterInfo();
+  SDValue Stack = DAG.getCopyFromReg(DAG.getEntryNode(), dl,
+                            RegInfo->getFrameRegister(MF), MVT::i32);
+  SDValue FrameToArgs = DAG.getNode(XCoreISD::FRAME_TO_ARGS_OFFSET, dl,
+                                    MVT::i32);
+  Stack = DAG.getNode(ISD::ADD, dl, MVT::i32, Stack, FrameToArgs);
+  Stack = DAG.getNode(ISD::ADD, dl, MVT::i32, Stack, Offset);
+
+  // R0=ExceptionPointerRegister R1=ExceptionSelectorRegister
+  // which leaves 2 caller saved registers, R2 & R3 for us to use.
+  unsigned StackReg = XCore::R2;
+  unsigned HandlerReg = XCore::R3;
+
+  SDValue OutChains[] = {
+    DAG.getCopyToReg(Chain, dl, StackReg, Stack),
+    DAG.getCopyToReg(Chain, dl, HandlerReg, Handler)
+  };
+
+  Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, OutChains, 2);
+
+  return DAG.getNode(XCoreISD::EH_RETURN, dl, MVT::Other, Chain,
+                     DAG.getRegister(StackReg, MVT::i32),
+                     DAG.getRegister(HandlerReg, MVT::i32));
+
+}
+
 SDValue XCoreTargetLowering::
 LowerADJUST_TRAMPOLINE(SDValue Op, SelectionDAG &DAG) const {
   return Op.getOperand(0);
index c4de86e22dc29f3c0d05b96123e61be83709836f..05e2a86a9b5aa0282009f7c6aba7df8fa442e1a5 100644 (file)
@@ -75,6 +75,10 @@ namespace llvm {
       // Offset from frame pointer to the first (possible) on-stack argument
       FRAME_TO_ARGS_OFFSET,
 
+      // Exception handler return. The stack is restored to the first
+      // followed by a jump to the second argument.
+      EH_RETURN,
+
       // Memory barrier.
       MEMBARRIER
     };
@@ -150,6 +154,7 @@ namespace llvm {
     // Lower Operand specifics
     SDValue LowerLOAD(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const;
+    SDValue LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
index 144d4fec24665da3efb9a714b0f3bd7ec873c1da..0aecca064f255661a0862143048e1f671fe8f119 100644 (file)
@@ -35,6 +35,11 @@ def XCoreBranchLink     : SDNode<"XCoreISD::BL",SDT_XCoreBranchLink,
 def XCoreRetsp : SDNode<"XCoreISD::RETSP", SDTBrind,
                       [SDNPHasChain, SDNPOptInGlue, SDNPMayLoad, SDNPVariadic]>;
 
+def SDT_XCoreEhRet : SDTypeProfile<0, 2,
+                            [SDTCisSameAs<0, 1>, SDTCisPtrTy<0>]>;
+def XCoreEhRet       : SDNode<"XCoreISD::EH_RETURN", SDT_XCoreEhRet,
+                         [SDNPHasChain, SDNPOptInGlue]>;
+
 def SDT_XCoreBR_JT    : SDTypeProfile<0, 2,
                                       [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>;
 
@@ -334,6 +339,11 @@ def FRAME_TO_ARGS_OFFSET : PseudoInstXCore<(outs GRRegs:$dst), (ins),
                                "# FRAME_TO_ARGS_OFFSET $dst",
                                [(set GRRegs:$dst, (frametoargsoffset))]>;
 
+let isReturn = 1, isTerminator = 1, isBarrier = 1 in
+def EH_RETURN : PseudoInstXCore<(outs), (ins GRRegs:$s, GRRegs:$handler),
+                               "# EH_RETURN $s, $handler",
+                               [(XCoreEhRet GRRegs:$s, GRRegs:$handler)]>;
+
 def LDWFI : PseudoInstXCore<(outs GRRegs:$dst), (ins MEMii:$addr),
                              "# LDWFI $dst, $addr",
                              [(set GRRegs:$dst, (load ADDRspii:$addr))]>;
index 55b77ac9e6b33c5f7bf519e61b26824a2dcf41ca..ac6453c8c7faf3d8fee8a643057beea8bae63d02 100644 (file)
@@ -21,7 +21,6 @@ entry:
   ret i8* %1
 }
 
-
 declare i8* @llvm.returnaddress(i32) nounwind readnone
 define i8* @RA0() nounwind {
 entry:
@@ -70,3 +69,64 @@ entry:
   %1 = call i8* @llvm.eh.dwarf.cfa(i32 0)
   ret i8* %1
 }
+
+declare void @llvm.eh.return.i32(i32, i8*)
+define i8* @EH0(i32 %offset, i8* %handler) {
+entry:
+; CHECK-LABEL: EH0
+; CHECK: ldc r2, 0
+; CHECK-NEXT: ldaw r3, sp[0]
+; CHECK-NEXT: add r2, r3, r2
+; CHECK-NEXT: add r2, r2, r0
+; CHECK-NEXT: mov r3, r1
+; CHECK-NEXT: set sp, r2
+; CHECK-NEXT: bau r3
+  call void @llvm.eh.return.i32(i32 %offset, i8* %handler)
+  unreachable
+}
+
+declare void @foo(...)
+define i8* @EH1(i32 %offset, i8* %handler) {
+entry:
+; CHECK-LABEL: EH1
+; CHECK: entsp 3
+; CHECK: stw r4, sp[2]
+; CHECK: stw r5, sp[1]
+; CHECK: mov r4, r1
+; CHECK-NEXT: mov r5, r0
+; CHECK-NEXT: bl foo
+; CHECK-NEXT: ldc r0, 12
+; CHECK-NEXT: ldaw r1, sp[0]
+; CHECK-NEXT: add r0, r1, r0
+; CHECK-NEXT: add r2, r0, r5
+; CHECK-NEXT: mov r3, r4
+; CHECK-NEXT: ldw r5, sp[1]
+; CHECK-NEXT: ldw r4, sp[2]
+; CHECK-NEXT: set sp, r2
+; CHECK-NEXT: bau r3
+  call void (...)* @foo()
+  call void @llvm.eh.return.i32(i32 %offset, i8* %handler)
+  unreachable
+}
+
+@offset = external constant i32
+@handler = external constant i8
+define i8* @EH2(i32 %r0, i32 %r1, i32 %r2, i32 %r3) {
+entry:
+; CHECK-LABEL: EH2
+; CHECK: entsp 1
+; CHECK: bl foo
+; CHECK-NEXT: ldw r0, cp[offset]
+; CHECK-NEXT: ldc r1, 4
+; CHECK-NEXT: ldaw r2, sp[0]
+; CHECK-NEXT: add r1, r2, r1
+; CHECK-NEXT: add r2, r1, r0
+; CHECK-NEXT: ldaw r11, cp[handler]
+; CHECK-NEXT: mov r3, r11
+; CHECK-NEXT: set sp, r2
+; CHECK-NEXT: bau r3
+  call void (...)* @foo()
+  %0 = load i32* @offset
+  call void @llvm.eh.return.i32(i32 %0, i8* @handler)
+  unreachable
+}