mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-09-25 17:20:48 +00:00
Add support for calls through function pointers in the 64-bit PowerPC SVR4 ABI.
Patch contributed by Ken Werner of IBM! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@91680 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -419,6 +419,9 @@ const char *PPCTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
|||||||
case PPCISD::Hi: return "PPCISD::Hi";
|
case PPCISD::Hi: return "PPCISD::Hi";
|
||||||
case PPCISD::Lo: return "PPCISD::Lo";
|
case PPCISD::Lo: return "PPCISD::Lo";
|
||||||
case PPCISD::TOC_ENTRY: return "PPCISD::TOC_ENTRY";
|
case PPCISD::TOC_ENTRY: return "PPCISD::TOC_ENTRY";
|
||||||
|
case PPCISD::TOC_RESTORE: return "PPCISD::TOC_RESTORE";
|
||||||
|
case PPCISD::LOAD: return "PPCISD::LOAD";
|
||||||
|
case PPCISD::LOAD_TOC: return "PPCISD::LOAD_TOC";
|
||||||
case PPCISD::DYNALLOC: return "PPCISD::DYNALLOC";
|
case PPCISD::DYNALLOC: return "PPCISD::DYNALLOC";
|
||||||
case PPCISD::GlobalBaseReg: return "PPCISD::GlobalBaseReg";
|
case PPCISD::GlobalBaseReg: return "PPCISD::GlobalBaseReg";
|
||||||
case PPCISD::SRL: return "PPCISD::SRL";
|
case PPCISD::SRL: return "PPCISD::SRL";
|
||||||
@@ -2428,7 +2431,7 @@ unsigned PrepareCall(SelectionDAG &DAG, SDValue &Callee, SDValue &InFlag,
|
|||||||
SDValue &Chain, DebugLoc dl, int SPDiff, bool isTailCall,
|
SDValue &Chain, DebugLoc dl, int SPDiff, bool isTailCall,
|
||||||
SmallVector<std::pair<unsigned, SDValue>, 8> &RegsToPass,
|
SmallVector<std::pair<unsigned, SDValue>, 8> &RegsToPass,
|
||||||
SmallVector<SDValue, 8> &Ops, std::vector<EVT> &NodeTys,
|
SmallVector<SDValue, 8> &Ops, std::vector<EVT> &NodeTys,
|
||||||
bool isSVR4ABI) {
|
bool isPPC64, bool isSVR4ABI) {
|
||||||
EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
|
||||||
NodeTys.push_back(MVT::Other); // Returns a chain
|
NodeTys.push_back(MVT::Other); // Returns a chain
|
||||||
NodeTys.push_back(MVT::Flag); // Returns a flag for retval copy to use.
|
NodeTys.push_back(MVT::Flag); // Returns a flag for retval copy to use.
|
||||||
@@ -2449,6 +2452,74 @@ unsigned PrepareCall(SelectionDAG &DAG, SDValue &Callee, SDValue &InFlag,
|
|||||||
// Otherwise, this is an indirect call. We have to use a MTCTR/BCTRL pair
|
// Otherwise, this is an indirect call. We have to use a MTCTR/BCTRL pair
|
||||||
// to do the call, we can't use PPCISD::CALL.
|
// to do the call, we can't use PPCISD::CALL.
|
||||||
SDValue MTCTROps[] = {Chain, Callee, InFlag};
|
SDValue MTCTROps[] = {Chain, Callee, InFlag};
|
||||||
|
|
||||||
|
if (isSVR4ABI && isPPC64) {
|
||||||
|
// Function pointers in the 64-bit SVR4 ABI do not point to the function
|
||||||
|
// entry point, but to the function descriptor (the function entry point
|
||||||
|
// address is part of the function descriptor though).
|
||||||
|
// The function descriptor is a three doubleword structure with the
|
||||||
|
// following fields: function entry point, TOC base address and
|
||||||
|
// environment pointer.
|
||||||
|
// Thus for a call through a function pointer, the following actions need
|
||||||
|
// to be performed:
|
||||||
|
// 1. Save the TOC of the caller in the TOC save area of its stack
|
||||||
|
// frame (this is done in LowerCall_Darwin()).
|
||||||
|
// 2. Load the address of the function entry point from the function
|
||||||
|
// descriptor.
|
||||||
|
// 3. Load the TOC of the callee from the function descriptor into r2.
|
||||||
|
// 4. Load the environment pointer from the function descriptor into
|
||||||
|
// r11.
|
||||||
|
// 5. Branch to the function entry point address.
|
||||||
|
// 6. On return of the callee, the TOC of the caller needs to be
|
||||||
|
// restored (this is done in FinishCall()).
|
||||||
|
//
|
||||||
|
// All those operations are flagged together to ensure that no other
|
||||||
|
// operations can be scheduled in between. E.g. without flagging the
|
||||||
|
// operations together, a TOC access in the caller could be scheduled
|
||||||
|
// between the load of the callee TOC and the branch to the callee, which
|
||||||
|
// results in the TOC access going through the TOC of the callee instead
|
||||||
|
// of going through the TOC of the caller, which leads to incorrect code.
|
||||||
|
|
||||||
|
// Load the address of the function entry point from the function
|
||||||
|
// descriptor.
|
||||||
|
SDVTList VTs = DAG.getVTList(MVT::i64, MVT::Other, MVT::Flag);
|
||||||
|
SDValue LoadFuncPtr = DAG.getNode(PPCISD::LOAD, dl, VTs, MTCTROps,
|
||||||
|
InFlag.getNode() ? 3 : 2);
|
||||||
|
Chain = LoadFuncPtr.getValue(1);
|
||||||
|
InFlag = LoadFuncPtr.getValue(2);
|
||||||
|
|
||||||
|
// Load environment pointer into r11.
|
||||||
|
// Offset of the environment pointer within the function descriptor.
|
||||||
|
SDValue PtrOff = DAG.getIntPtrConstant(16);
|
||||||
|
|
||||||
|
SDValue AddPtr = DAG.getNode(ISD::ADD, dl, MVT::i64, Callee, PtrOff);
|
||||||
|
SDValue LoadEnvPtr = DAG.getNode(PPCISD::LOAD, dl, VTs, Chain, AddPtr,
|
||||||
|
InFlag);
|
||||||
|
Chain = LoadEnvPtr.getValue(1);
|
||||||
|
InFlag = LoadEnvPtr.getValue(2);
|
||||||
|
|
||||||
|
SDValue EnvVal = DAG.getCopyToReg(Chain, dl, PPC::X11, LoadEnvPtr,
|
||||||
|
InFlag);
|
||||||
|
Chain = EnvVal.getValue(0);
|
||||||
|
InFlag = EnvVal.getValue(1);
|
||||||
|
|
||||||
|
// Load TOC of the callee into r2. We are using a target-specific load
|
||||||
|
// with r2 hard coded, because the result of a target-independent load
|
||||||
|
// would never go directly into r2, since r2 is a reserved register (which
|
||||||
|
// prevents the register allocator from allocating it), resulting in an
|
||||||
|
// additional register being allocated and an unnecessary move instruction
|
||||||
|
// being generated.
|
||||||
|
VTs = DAG.getVTList(MVT::Other, MVT::Flag);
|
||||||
|
SDValue LoadTOCPtr = DAG.getNode(PPCISD::LOAD_TOC, dl, VTs, Chain,
|
||||||
|
Callee, InFlag);
|
||||||
|
Chain = LoadTOCPtr.getValue(0);
|
||||||
|
InFlag = LoadTOCPtr.getValue(1);
|
||||||
|
|
||||||
|
MTCTROps[0] = Chain;
|
||||||
|
MTCTROps[1] = LoadFuncPtr;
|
||||||
|
MTCTROps[2] = InFlag;
|
||||||
|
}
|
||||||
|
|
||||||
Chain = DAG.getNode(PPCISD::MTCTR, dl, NodeTys, MTCTROps,
|
Chain = DAG.getNode(PPCISD::MTCTR, dl, NodeTys, MTCTROps,
|
||||||
2 + (InFlag.getNode() != 0));
|
2 + (InFlag.getNode() != 0));
|
||||||
InFlag = Chain.getValue(1);
|
InFlag = Chain.getValue(1);
|
||||||
@@ -2523,6 +2594,7 @@ PPCTargetLowering::FinishCall(CallingConv::ID CallConv, DebugLoc dl,
|
|||||||
SmallVector<SDValue, 8> Ops;
|
SmallVector<SDValue, 8> Ops;
|
||||||
unsigned CallOpc = PrepareCall(DAG, Callee, InFlag, Chain, dl, SPDiff,
|
unsigned CallOpc = PrepareCall(DAG, Callee, InFlag, Chain, dl, SPDiff,
|
||||||
isTailCall, RegsToPass, Ops, NodeTys,
|
isTailCall, RegsToPass, Ops, NodeTys,
|
||||||
|
PPCSubTarget.isPPC64(),
|
||||||
PPCSubTarget.isSVR4ABI());
|
PPCSubTarget.isSVR4ABI());
|
||||||
|
|
||||||
// When performing tail call optimization the callee pops its arguments off
|
// When performing tail call optimization the callee pops its arguments off
|
||||||
@@ -2569,8 +2641,23 @@ PPCTargetLowering::FinishCall(CallingConv::ID CallConv, DebugLoc dl,
|
|||||||
// stack frame. If caller and callee belong to the same module (and have the
|
// stack frame. If caller and callee belong to the same module (and have the
|
||||||
// same TOC), the NOP will remain unchanged.
|
// same TOC), the NOP will remain unchanged.
|
||||||
if (!isTailCall && PPCSubTarget.isSVR4ABI()&& PPCSubTarget.isPPC64()) {
|
if (!isTailCall && PPCSubTarget.isSVR4ABI()&& PPCSubTarget.isPPC64()) {
|
||||||
// Insert NOP.
|
SDVTList VTs = DAG.getVTList(MVT::Other, MVT::Flag);
|
||||||
InFlag = DAG.getNode(PPCISD::NOP, dl, MVT::Flag, InFlag);
|
if (CallOpc == PPCISD::BCTRL_SVR4) {
|
||||||
|
// This is a call through a function pointer.
|
||||||
|
// Restore the caller TOC from the save area into R2.
|
||||||
|
// See PrepareCall() for more information about calls through function
|
||||||
|
// pointers in the 64-bit SVR4 ABI.
|
||||||
|
// We are using a target-specific load with r2 hard coded, because the
|
||||||
|
// result of a target-independent load would never go directly into r2,
|
||||||
|
// since r2 is a reserved register (which prevents the register allocator
|
||||||
|
// from allocating it), resulting in an additional register being
|
||||||
|
// allocated and an unnecessary move instruction being generated.
|
||||||
|
Chain = DAG.getNode(PPCISD::TOC_RESTORE, dl, VTs, Chain, InFlag);
|
||||||
|
InFlag = Chain.getValue(1);
|
||||||
|
} else {
|
||||||
|
// Otherwise insert NOP.
|
||||||
|
InFlag = DAG.getNode(PPCISD::NOP, dl, MVT::Flag, InFlag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, true),
|
Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, true),
|
||||||
@@ -3123,6 +3210,21 @@ PPCTargetLowering::LowerCall_Darwin(SDValue Chain, SDValue Callee,
|
|||||||
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
|
||||||
&MemOpChains[0], MemOpChains.size());
|
&MemOpChains[0], MemOpChains.size());
|
||||||
|
|
||||||
|
// Check if this is an indirect call (MTCTR/BCTRL).
|
||||||
|
// See PrepareCall() for more information about calls through function
|
||||||
|
// pointers in the 64-bit SVR4 ABI.
|
||||||
|
if (!isTailCall && isPPC64 && PPCSubTarget.isSVR4ABI() &&
|
||||||
|
!dyn_cast<GlobalAddressSDNode>(Callee) &&
|
||||||
|
!dyn_cast<ExternalSymbolSDNode>(Callee) &&
|
||||||
|
!isBLACompatibleAddress(Callee, DAG)) {
|
||||||
|
// Load r2 into a virtual register and store it to the TOC save area.
|
||||||
|
SDValue Val = DAG.getCopyFromReg(Chain, dl, PPC::X2, MVT::i64);
|
||||||
|
// TOC save area offset.
|
||||||
|
SDValue PtrOff = DAG.getIntPtrConstant(40);
|
||||||
|
SDValue AddPtr = DAG.getNode(ISD::ADD, dl, PtrVT, StackPtr, PtrOff);
|
||||||
|
Chain = DAG.getStore(Val.getValue(1), dl, Val, AddPtr, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Build a sequence of copy-to-reg nodes chained together with token chain
|
// Build a sequence of copy-to-reg nodes chained together with token chain
|
||||||
// and flag operands which copy the outgoing args into the appropriate regs.
|
// and flag operands which copy the outgoing args into the appropriate regs.
|
||||||
SDValue InFlag;
|
SDValue InFlag;
|
||||||
|
@@ -61,6 +61,21 @@ namespace llvm {
|
|||||||
|
|
||||||
TOC_ENTRY,
|
TOC_ENTRY,
|
||||||
|
|
||||||
|
/// The following three target-specific nodes are used for calls through
|
||||||
|
/// function pointers in the 64-bit SVR4 ABI.
|
||||||
|
|
||||||
|
/// Restore the TOC from the TOC save area of the current stack frame.
|
||||||
|
/// This is basically a hard coded load instruction which additionally
|
||||||
|
/// takes/produces a flag.
|
||||||
|
TOC_RESTORE,
|
||||||
|
|
||||||
|
/// Like a regular LOAD but additionally taking/producing a flag.
|
||||||
|
LOAD,
|
||||||
|
|
||||||
|
/// LOAD into r2 (also taking/producing a flag). Like TOC_RESTORE, this is
|
||||||
|
/// a hard coded load instruction.
|
||||||
|
LOAD_TOC,
|
||||||
|
|
||||||
/// OPRC, CHAIN = DYNALLOC(CHAIN, NEGSIZE, FRAME_INDEX)
|
/// OPRC, CHAIN = DYNALLOC(CHAIN, NEGSIZE, FRAME_INDEX)
|
||||||
/// This instruction is lowered in PPCRegisterInfo::eliminateFrameIndex to
|
/// This instruction is lowered in PPCRegisterInfo::eliminateFrameIndex to
|
||||||
/// compute an allocation on the stack.
|
/// compute an allocation on the stack.
|
||||||
|
@@ -559,6 +559,14 @@ def LDtoc: DSForm_1<58, 0, (outs G8RC:$rD), (ins tocentry:$disp, G8RC:$reg),
|
|||||||
"ld $rD, $disp($reg)", LdStLD,
|
"ld $rD, $disp($reg)", LdStLD,
|
||||||
[(set G8RC:$rD,
|
[(set G8RC:$rD,
|
||||||
(PPCtoc_entry tglobaladdr:$disp, G8RC:$reg))]>, isPPC64;
|
(PPCtoc_entry tglobaladdr:$disp, G8RC:$reg))]>, isPPC64;
|
||||||
|
let RST = 2, DS = 8 in
|
||||||
|
def LDinto_toc: DSForm_1<58, 0, (outs), (ins G8RC:$reg),
|
||||||
|
"ld 2, 8($reg)", LdStLD,
|
||||||
|
[(PPCload_toc G8RC:$reg)]>, isPPC64;
|
||||||
|
let RST = 2, DS = 40, RA = 1 in
|
||||||
|
def LDtoc_restore : DSForm_1<58, 0, (outs), (ins),
|
||||||
|
"ld 2, 40(1)", LdStLD,
|
||||||
|
[]>, isPPC64;
|
||||||
def LDX : XForm_1<31, 21, (outs G8RC:$rD), (ins memrr:$src),
|
def LDX : XForm_1<31, 21, (outs G8RC:$rD), (ins memrr:$src),
|
||||||
"ldx $rD, $src", LdStLD,
|
"ldx $rD, $src", LdStLD,
|
||||||
[(set G8RC:$rD, (load xaddr:$src))]>, isPPC64;
|
[(set G8RC:$rD, (load xaddr:$src))]>, isPPC64;
|
||||||
@@ -571,6 +579,13 @@ def LDU : DSForm_1<58, 1, (outs G8RC:$rD, ptr_rc:$ea_result), (ins memrix:$addr
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def : Pat<(PPCtoc_restore),
|
||||||
|
(LDtoc_restore)>;
|
||||||
|
def : Pat<(PPCload ixaddr:$src),
|
||||||
|
(LD ixaddr:$src)>;
|
||||||
|
def : Pat<(PPCload xaddr:$src),
|
||||||
|
(LDX xaddr:$src)>;
|
||||||
|
|
||||||
let PPC970_Unit = 2 in {
|
let PPC970_Unit = 2 in {
|
||||||
// Truncating stores.
|
// Truncating stores.
|
||||||
def STB8 : DForm_1<38, (outs), (ins G8RC:$rS, memri:$src),
|
def STB8 : DForm_1<38, (outs), (ins G8RC:$rS, memri:$src),
|
||||||
|
@@ -115,6 +115,12 @@ def PPCcall_Darwin : SDNode<"PPCISD::CALL_Darwin", SDT_PPCCall,
|
|||||||
def PPCcall_SVR4 : SDNode<"PPCISD::CALL_SVR4", SDT_PPCCall,
|
def PPCcall_SVR4 : SDNode<"PPCISD::CALL_SVR4", SDT_PPCCall,
|
||||||
[SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>;
|
[SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>;
|
||||||
def PPCnop : SDNode<"PPCISD::NOP", SDT_PPCnop, [SDNPInFlag, SDNPOutFlag]>;
|
def PPCnop : SDNode<"PPCISD::NOP", SDT_PPCnop, [SDNPInFlag, SDNPOutFlag]>;
|
||||||
|
def PPCload : SDNode<"PPCISD::LOAD", SDTypeProfile<1, 1, []>,
|
||||||
|
[SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>;
|
||||||
|
def PPCload_toc : SDNode<"PPCISD::LOAD_TOC", SDTypeProfile<0, 1, []>,
|
||||||
|
[SDNPHasChain, SDNPInFlag, SDNPOutFlag]>;
|
||||||
|
def PPCtoc_restore : SDNode<"PPCISD::TOC_RESTORE", SDTypeProfile<0, 0, []>,
|
||||||
|
[SDNPHasChain, SDNPInFlag, SDNPOutFlag]>;
|
||||||
def PPCmtctr : SDNode<"PPCISD::MTCTR", SDT_PPCCall,
|
def PPCmtctr : SDNode<"PPCISD::MTCTR", SDT_PPCCall,
|
||||||
[SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>;
|
[SDNPHasChain, SDNPOptInFlag, SDNPOutFlag]>;
|
||||||
def PPCbctrl_Darwin : SDNode<"PPCISD::BCTRL_Darwin", SDTNone,
|
def PPCbctrl_Darwin : SDNode<"PPCISD::BCTRL_Darwin", SDTNone,
|
||||||
|
Reference in New Issue
Block a user