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();
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;
}
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);
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);
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);
// 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
};
// 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;
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>]>;
"# 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))]>;
ret i8* %1
}
-
declare i8* @llvm.returnaddress(i32) nounwind readnone
define i8* @RA0() nounwind {
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
+}