mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-07-04 18:24:38 +00:00
[Sparc] Correctly handle call to functions with ReturnsTwice attribute.
In sparc, setjmp stores only the registers %fp, %sp, %i7 and %o7. longjmp restores the stack, and the callee-saved registers (all local/in registers: %i0-%i7, %l0-%l7) using the stored %fp and register windows. However, this does not guarantee that the longjmp will restore the registers, as they were when the setjmp was called. This is because these registers may be clobbered after returning from setjmp, but before calling longjmp. This patch prevents the registers %i0-%i5, %l0-l7 to live across the setjmp call using the register mask. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@190033 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@ -123,3 +123,8 @@ def CSR : CalleeSavedRegs<(add)> {
|
|||||||
let OtherPreserved = (add (sequence "I%u", 0, 7),
|
let OtherPreserved = (add (sequence "I%u", 0, 7),
|
||||||
(sequence "L%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);
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "SparcISelLowering.h"
|
#include "SparcISelLowering.h"
|
||||||
#include "SparcMachineFunctionInfo.h"
|
#include "SparcMachineFunctionInfo.h"
|
||||||
|
#include "SparcRegisterInfo.h"
|
||||||
#include "SparcTargetMachine.h"
|
#include "SparcTargetMachine.h"
|
||||||
#include "MCTargetDesc/SparcBaseInfo.h"
|
#include "MCTargetDesc/SparcBaseInfo.h"
|
||||||
#include "llvm/CodeGen/CallingConvLower.h"
|
#include "llvm/CodeGen/CallingConvLower.h"
|
||||||
@ -648,6 +649,27 @@ SparcTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
return LowerCall_32(CLI, InVals);
|
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<GlobalAddressSDNode>(Callee)) {
|
||||||
|
CalleeFn = dyn_cast<Function>(G->getGlobal());
|
||||||
|
} else if (ExternalSymbolSDNode *E =
|
||||||
|
dyn_cast<ExternalSymbolSDNode>(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.
|
// Lower a call for the 32-bit ABI.
|
||||||
SDValue
|
SDValue
|
||||||
SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
|
SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
|
||||||
@ -861,6 +883,7 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned SRetArgSize = (hasStructRetAttr)? getSRetArgSize(DAG, Callee):0;
|
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)
|
// 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.
|
// 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()));
|
RegsToPass[i].second.getValueType()));
|
||||||
|
|
||||||
// Add a register mask operand representing the call-preserved registers.
|
// Add a register mask operand representing the call-preserved registers.
|
||||||
const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo();
|
const SparcRegisterInfo *TRI =
|
||||||
const uint32_t *Mask = TRI->getCallPreservedMask(CallConv);
|
((const SparcTargetMachine&)getTargetMachine()).getRegisterInfo();
|
||||||
|
const uint32_t *Mask = ((hasReturnsTwice)
|
||||||
|
? TRI->getRTCallPreservedMask(CallConv)
|
||||||
|
: TRI->getCallPreservedMask(CallConv));
|
||||||
assert(Mask && "Missing call preserved mask for calling convention");
|
assert(Mask && "Missing call preserved mask for calling convention");
|
||||||
Ops.push_back(DAG.getRegisterMask(Mask));
|
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.
|
// turn it into a TargetGlobalAddress node so that legalize doesn't hack it.
|
||||||
// Likewise ExternalSymbol -> TargetExternalSymbol.
|
// Likewise ExternalSymbol -> TargetExternalSymbol.
|
||||||
SDValue Callee = CLI.Callee;
|
SDValue Callee = CLI.Callee;
|
||||||
|
bool hasReturnsTwice = hasReturnsTwiceAttr(DAG, Callee, CLI.CS);
|
||||||
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee))
|
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee))
|
||||||
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, getPointerTy());
|
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, getPointerTy());
|
||||||
else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee))
|
else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee))
|
||||||
@ -1139,8 +1166,11 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
|
|||||||
RegsToPass[i].second.getValueType()));
|
RegsToPass[i].second.getValueType()));
|
||||||
|
|
||||||
// Add a register mask operand representing the call-preserved registers.
|
// Add a register mask operand representing the call-preserved registers.
|
||||||
const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo();
|
const SparcRegisterInfo *TRI =
|
||||||
const uint32_t *Mask = TRI->getCallPreservedMask(CLI.CallConv);
|
((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");
|
assert(Mask && "Missing call preserved mask for calling convention");
|
||||||
Ops.push_back(DAG.getRegisterMask(Mask));
|
Ops.push_back(DAG.getRegisterMask(Mask));
|
||||||
|
|
||||||
|
@ -48,6 +48,11 @@ SparcRegisterInfo::getCallPreservedMask(CallingConv::ID CC) const {
|
|||||||
return CSR_RegMask;
|
return CSR_RegMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t*
|
||||||
|
SparcRegisterInfo::getRTCallPreservedMask(CallingConv::ID CC) const {
|
||||||
|
return RTCSR_RegMask;
|
||||||
|
}
|
||||||
|
|
||||||
BitVector SparcRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
|
BitVector SparcRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
|
||||||
BitVector Reserved(getNumRegs());
|
BitVector Reserved(getNumRegs());
|
||||||
// FIXME: G1 reserved for now for large imm generation by frame code.
|
// FIXME: G1 reserved for now for large imm generation by frame code.
|
||||||
|
@ -34,6 +34,8 @@ struct SparcRegisterInfo : public SparcGenRegisterInfo {
|
|||||||
const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const;
|
const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const;
|
||||||
const uint32_t* getCallPreservedMask(CallingConv::ID CC) const;
|
const uint32_t* getCallPreservedMask(CallingConv::ID CC) const;
|
||||||
|
|
||||||
|
const uint32_t* getRTCallPreservedMask(CallingConv::ID CC) const;
|
||||||
|
|
||||||
BitVector getReservedRegs(const MachineFunction &MF) const;
|
BitVector getReservedRegs(const MachineFunction &MF) const;
|
||||||
|
|
||||||
const TargetRegisterClass *getPointerRegClass(const MachineFunction &MF,
|
const TargetRegisterClass *getPointerRegClass(const MachineFunction &MF,
|
||||||
|
70
test/CodeGen/SPARC/setjmp.ll
Normal file
70
test/CodeGen/SPARC/setjmp.ll
Normal file
@ -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}
|
Reference in New Issue
Block a user