diff --git a/lib/Target/Sparc/SparcCallingConv.td b/lib/Target/Sparc/SparcCallingConv.td index 181165d6ddd..acd4ec21de4 100644 --- a/lib/Target/Sparc/SparcCallingConv.td +++ b/lib/Target/Sparc/SparcCallingConv.td @@ -123,3 +123,8 @@ def CSR : CalleeSavedRegs<(add)> { let OtherPreserved = (add (sequence "I%u", 0, 7), (sequence "L%u", 0, 7)); } + +// Callee-saved registers for calls with ReturnsTwice attribute. +def RTCSR : CalleeSavedRegs<(add)> { + let OtherPreserved = (add I6, I7); +} diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp index e646521f60f..641ab6cd259 100644 --- a/lib/Target/Sparc/SparcISelLowering.cpp +++ b/lib/Target/Sparc/SparcISelLowering.cpp @@ -14,6 +14,7 @@ #include "SparcISelLowering.h" #include "SparcMachineFunctionInfo.h" +#include "SparcRegisterInfo.h" #include "SparcTargetMachine.h" #include "MCTargetDesc/SparcBaseInfo.h" #include "llvm/CodeGen/CallingConvLower.h" @@ -648,6 +649,27 @@ SparcTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, return LowerCall_32(CLI, InVals); } +static bool hasReturnsTwiceAttr(SelectionDAG &DAG, SDValue Callee, + ImmutableCallSite *CS) { + if (CS) + return CS->hasFnAttr(Attribute::ReturnsTwice); + + const Function *CalleeFn = 0; + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + CalleeFn = dyn_cast(G->getGlobal()); + } else if (ExternalSymbolSDNode *E = + dyn_cast(Callee)) { + const Function *Fn = DAG.getMachineFunction().getFunction(); + const Module *M = Fn->getParent(); + const char *CalleeName = E->getSymbol(); + CalleeFn = M->getFunction(CalleeName); + } + + if (!CalleeFn) + return false; + return CalleeFn->hasFnAttribute(Attribute::ReturnsTwice); +} + // Lower a call for the 32-bit ABI. SDValue SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI, @@ -861,6 +883,7 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI, } unsigned SRetArgSize = (hasStructRetAttr)? getSRetArgSize(DAG, Callee):0; + bool hasReturnsTwice = hasReturnsTwiceAttr(DAG, Callee, CLI.CS); // If the callee is a GlobalAddress node (quite common, every direct call is) // turn it into a TargetGlobalAddress node so that legalize doesn't hack it. @@ -882,8 +905,11 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI, RegsToPass[i].second.getValueType())); // Add a register mask operand representing the call-preserved registers. - const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo(); - const uint32_t *Mask = TRI->getCallPreservedMask(CallConv); + const SparcRegisterInfo *TRI = + ((const SparcTargetMachine&)getTargetMachine()).getRegisterInfo(); + const uint32_t *Mask = ((hasReturnsTwice) + ? TRI->getRTCallPreservedMask(CallConv) + : TRI->getCallPreservedMask(CallConv)); assert(Mask && "Missing call preserved mask for calling convention"); Ops.push_back(DAG.getRegisterMask(Mask)); @@ -1125,6 +1151,7 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI, // turn it into a TargetGlobalAddress node so that legalize doesn't hack it. // Likewise ExternalSymbol -> TargetExternalSymbol. SDValue Callee = CLI.Callee; + bool hasReturnsTwice = hasReturnsTwiceAttr(DAG, Callee, CLI.CS); if (GlobalAddressSDNode *G = dyn_cast(Callee)) Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, getPointerTy()); else if (ExternalSymbolSDNode *E = dyn_cast(Callee)) @@ -1139,8 +1166,11 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI, RegsToPass[i].second.getValueType())); // Add a register mask operand representing the call-preserved registers. - const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo(); - const uint32_t *Mask = TRI->getCallPreservedMask(CLI.CallConv); + const SparcRegisterInfo *TRI = + ((const SparcTargetMachine&)getTargetMachine()).getRegisterInfo(); + const uint32_t *Mask = ((hasReturnsTwice) + ? TRI->getRTCallPreservedMask(CLI.CallConv) + : TRI->getCallPreservedMask(CLI.CallConv)); assert(Mask && "Missing call preserved mask for calling convention"); Ops.push_back(DAG.getRegisterMask(Mask)); diff --git a/lib/Target/Sparc/SparcRegisterInfo.cpp b/lib/Target/Sparc/SparcRegisterInfo.cpp index 09f52fb9225..8e14dd95f68 100644 --- a/lib/Target/Sparc/SparcRegisterInfo.cpp +++ b/lib/Target/Sparc/SparcRegisterInfo.cpp @@ -48,6 +48,11 @@ SparcRegisterInfo::getCallPreservedMask(CallingConv::ID CC) const { return CSR_RegMask; } +const uint32_t* +SparcRegisterInfo::getRTCallPreservedMask(CallingConv::ID CC) const { + return RTCSR_RegMask; +} + BitVector SparcRegisterInfo::getReservedRegs(const MachineFunction &MF) const { BitVector Reserved(getNumRegs()); // FIXME: G1 reserved for now for large imm generation by frame code. diff --git a/lib/Target/Sparc/SparcRegisterInfo.h b/lib/Target/Sparc/SparcRegisterInfo.h index ae056cdcf29..503229d9bd1 100644 --- a/lib/Target/Sparc/SparcRegisterInfo.h +++ b/lib/Target/Sparc/SparcRegisterInfo.h @@ -34,6 +34,8 @@ struct SparcRegisterInfo : public SparcGenRegisterInfo { const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const; const uint32_t* getCallPreservedMask(CallingConv::ID CC) const; + const uint32_t* getRTCallPreservedMask(CallingConv::ID CC) const; + BitVector getReservedRegs(const MachineFunction &MF) const; const TargetRegisterClass *getPointerRegClass(const MachineFunction &MF, diff --git a/test/CodeGen/SPARC/setjmp.ll b/test/CodeGen/SPARC/setjmp.ll new file mode 100644 index 00000000000..f377f458b69 --- /dev/null +++ b/test/CodeGen/SPARC/setjmp.ll @@ -0,0 +1,70 @@ +;RUN: llc -march=sparc < %s | FileCheck %s +;RUN: llc -march=sparcv9 < %s | FileCheck %s --check-prefix=V9 + + +%0 = type { [32 x i32] } +%struct.jmpbuf_env = type { i32, i32, [1 x %struct.__jmp_buf_tag], i32 } +%struct.__jmp_buf_tag = type { [3 x i32], i32, %0 } + +@jenv = common unnamed_addr global %struct.jmpbuf_env* null +@.cst = linker_private unnamed_addr constant [30 x i8] c"in bar with jmp_buf's id: %d\0A\00", align 64 + +; CHECK-LABEL: foo +; CHECK-DAG: st {{.+}}, [%i0] +; CHECK-DAG: st {{.+}}, [%i0+4] +; CHECK: call _setjmp +; CHECK: ld [%fp+{{.+}}], %[[R:[gilo][0-7]]] +; CHECK: st %o0, [%[[R]]+{{.+}}] + +; V9-LABEL: foo +; V9-DAG: st {{.+}}, [%i0] +; V9-DAG: st {{.+}}, [%i0+4] +; V9: call _setjmp +; V9: ldx [%fp+{{.+}}], %[[R:[gilo][0-7]]] +; V9: st %o0, [%[[R]]+{{.+}}] + +; Function Attrs: nounwind +define i32 @foo(%struct.jmpbuf_env* byval %inbuf) #0 { +entry: + %0 = getelementptr inbounds %struct.jmpbuf_env* %inbuf, i32 0, i32 0 + store i32 0, i32* %0, align 4, !tbaa !2 + %1 = getelementptr inbounds %struct.jmpbuf_env* %inbuf, i32 0, i32 1 + store i32 1, i32* %1, align 4, !tbaa !2 + %2 = getelementptr inbounds %struct.jmpbuf_env* %inbuf, i32 0, i32 2, i32 0 + %3 = call i32 @_setjmp(%struct.__jmp_buf_tag* %2) #2 + %4 = getelementptr inbounds %struct.jmpbuf_env* %inbuf, i32 0, i32 3 + store i32 %3, i32* %4, align 4, !tbaa !2 + store %struct.jmpbuf_env* %inbuf, %struct.jmpbuf_env** @jenv, align 4, !tbaa !0 + %5 = load i32* %1, align 4, !tbaa !2 + %6 = icmp eq i32 %5, 1 + %7 = icmp eq i32 %3, 0 + %or.cond = and i1 %6, %7 + br i1 %or.cond, label %"4.i", label %bar.exit + +"4.i": ; preds = %entry + call void @longjmp(%struct.__jmp_buf_tag* %2, i32 0) #1 + unreachable + +bar.exit: ; preds = %entry + %8 = load i32* %0, align 4, !tbaa !2 + %9 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([30 x i8]* @.cst, i32 0, i32 0), i32 %8) #0 + ret i32 0 +} + +; Function Attrs: nounwind returns_twice +declare i32 @_setjmp(%struct.__jmp_buf_tag*) #2 + +; Function Attrs: noreturn nounwind +declare void @longjmp(%struct.__jmp_buf_tag*, i32) #1 + +; Function Attrs: nounwind +declare i32 @printf(i8* nocapture, ...) #0 + + +attributes #0 = { nounwind } +attributes #1 = { noreturn nounwind } +attributes #2 = { nounwind returns_twice } + +!0 = metadata !{metadata !"alias set 6: struct.jmpbuf_env*", metadata !1} +!1 = metadata !{metadata !1} +!2 = metadata !{metadata !"alias set 3: int", metadata !1}