forked from OSchip/llvm-project
[AArch64] Add support for dllimport of values and functions
Previously, the dllimport attribute did the right thing in terms of treating it as a pointer to a value, but this makes sure the names get mangled properly, and calls to such functions load the function from the __imp_ pointer. This is based on SVN r212431 and r212430 where the same was implemented for ARM. Differential Revision: https://reviews.llvm.org/D38530 llvm-svn: 316555
This commit is contained in:
parent
8a752b77a2
commit
373c8efa1e
|
@ -3487,6 +3487,10 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
|
|||
AArch64II::MO_GOT) {
|
||||
Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, AArch64II::MO_GOT);
|
||||
Callee = DAG.getNode(AArch64ISD::LOADgot, DL, PtrVT, Callee);
|
||||
} else if (Subtarget->isTargetCOFF() && GV->hasDLLImportStorageClass()) {
|
||||
assert(Subtarget->isTargetWindows() &&
|
||||
"Windows is the only supported COFF target");
|
||||
Callee = getGOT(G, DAG, AArch64II::MO_DLLIMPORT);
|
||||
} else {
|
||||
const GlobalValue *GV = G->getGlobal();
|
||||
Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, 0);
|
||||
|
@ -3687,11 +3691,12 @@ SDValue AArch64TargetLowering::getTargetNode(BlockAddressSDNode* N, EVT Ty,
|
|||
|
||||
// (loadGOT sym)
|
||||
template <class NodeTy>
|
||||
SDValue AArch64TargetLowering::getGOT(NodeTy *N, SelectionDAG &DAG) const {
|
||||
SDValue AArch64TargetLowering::getGOT(NodeTy *N, SelectionDAG &DAG,
|
||||
unsigned Flags) const {
|
||||
DEBUG(dbgs() << "AArch64TargetLowering::getGOT\n");
|
||||
SDLoc DL(N);
|
||||
EVT Ty = getPointerTy(DAG.getDataLayout());
|
||||
SDValue GotAddr = getTargetNode(N, Ty, DAG, AArch64II::MO_GOT);
|
||||
SDValue GotAddr = getTargetNode(N, Ty, DAG, AArch64II::MO_GOT | Flags);
|
||||
// FIXME: Once remat is capable of dealing with instructions with register
|
||||
// operands, expand this into two nodes instead of using a wrapper node.
|
||||
return DAG.getNode(AArch64ISD::LOADgot, DL, Ty, GotAddr);
|
||||
|
@ -3699,29 +3704,30 @@ SDValue AArch64TargetLowering::getGOT(NodeTy *N, SelectionDAG &DAG) const {
|
|||
|
||||
// (wrapper %highest(sym), %higher(sym), %hi(sym), %lo(sym))
|
||||
template <class NodeTy>
|
||||
SDValue AArch64TargetLowering::getAddrLarge(NodeTy *N, SelectionDAG &DAG)
|
||||
const {
|
||||
SDValue AArch64TargetLowering::getAddrLarge(NodeTy *N, SelectionDAG &DAG,
|
||||
unsigned Flags) const {
|
||||
DEBUG(dbgs() << "AArch64TargetLowering::getAddrLarge\n");
|
||||
SDLoc DL(N);
|
||||
EVT Ty = getPointerTy(DAG.getDataLayout());
|
||||
const unsigned char MO_NC = AArch64II::MO_NC;
|
||||
return DAG.getNode(
|
||||
AArch64ISD::WrapperLarge, DL, Ty,
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G3),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G2 | MO_NC),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G1 | MO_NC),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G0 | MO_NC));
|
||||
AArch64ISD::WrapperLarge, DL, Ty,
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G3 | Flags),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G2 | MO_NC | Flags),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G1 | MO_NC | Flags),
|
||||
getTargetNode(N, Ty, DAG, AArch64II::MO_G0 | MO_NC | Flags));
|
||||
}
|
||||
|
||||
// (addlow (adrp %hi(sym)) %lo(sym))
|
||||
template <class NodeTy>
|
||||
SDValue AArch64TargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG) const {
|
||||
SDValue AArch64TargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG,
|
||||
unsigned Flags) const {
|
||||
DEBUG(dbgs() << "AArch64TargetLowering::getAddr\n");
|
||||
SDLoc DL(N);
|
||||
EVT Ty = getPointerTy(DAG.getDataLayout());
|
||||
SDValue Hi = getTargetNode(N, Ty, DAG, AArch64II::MO_PAGE);
|
||||
SDValue Hi = getTargetNode(N, Ty, DAG, AArch64II::MO_PAGE | Flags);
|
||||
SDValue Lo = getTargetNode(N, Ty, DAG,
|
||||
AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
|
||||
AArch64II::MO_PAGEOFF | AArch64II::MO_NC | Flags);
|
||||
SDValue ADRP = DAG.getNode(AArch64ISD::ADRP, DL, Ty, Hi);
|
||||
return DAG.getNode(AArch64ISD::ADDlow, DL, Ty, ADRP, Lo);
|
||||
}
|
||||
|
@ -3730,6 +3736,9 @@ SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op,
|
|||
SelectionDAG &DAG) const {
|
||||
GlobalAddressSDNode *GN = cast<GlobalAddressSDNode>(Op);
|
||||
const GlobalValue *GV = GN->getGlobal();
|
||||
const AArch64II::TOF TargetFlags =
|
||||
(GV->hasDLLImportStorageClass() ? AArch64II::MO_DLLIMPORT
|
||||
: AArch64II::MO_NO_FLAG);
|
||||
unsigned char OpFlags =
|
||||
Subtarget->ClassifyGlobalReference(GV, getTargetMachine());
|
||||
|
||||
|
@ -3738,14 +3747,21 @@ SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op,
|
|||
|
||||
// This also catches the large code model case for Darwin.
|
||||
if ((OpFlags & AArch64II::MO_GOT) != 0) {
|
||||
return getGOT(GN, DAG);
|
||||
return getGOT(GN, DAG, TargetFlags);
|
||||
}
|
||||
|
||||
SDValue Result;
|
||||
if (getTargetMachine().getCodeModel() == CodeModel::Large) {
|
||||
return getAddrLarge(GN, DAG);
|
||||
Result = getAddrLarge(GN, DAG, TargetFlags);
|
||||
} else {
|
||||
return getAddr(GN, DAG);
|
||||
Result = getAddr(GN, DAG, TargetFlags);
|
||||
}
|
||||
EVT PtrVT = getPointerTy(DAG.getDataLayout());
|
||||
SDLoc DL(GN);
|
||||
if (GV->hasDLLImportStorageClass())
|
||||
Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result,
|
||||
MachinePointerInfo::getGOT(DAG.getMachineFunction()));
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// \brief Convert a TLS address reference into the correct sequence of loads
|
||||
|
|
|
@ -538,10 +538,12 @@ private:
|
|||
unsigned Flag) const;
|
||||
SDValue getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG,
|
||||
unsigned Flag) const;
|
||||
template <class NodeTy> SDValue getGOT(NodeTy *N, SelectionDAG &DAG) const;
|
||||
template <class NodeTy>
|
||||
SDValue getAddrLarge(NodeTy *N, SelectionDAG &DAG) const;
|
||||
template <class NodeTy> SDValue getAddr(NodeTy *N, SelectionDAG &DAG) const;
|
||||
SDValue getGOT(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const;
|
||||
template <class NodeTy>
|
||||
SDValue getAddrLarge(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const;
|
||||
template <class NodeTy>
|
||||
SDValue getAddr(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const;
|
||||
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerDarwinGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/IR/Mangler.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
using namespace llvm;
|
||||
|
||||
|
@ -33,7 +35,25 @@ AArch64MCInstLower::AArch64MCInstLower(MCContext &ctx, AsmPrinter &printer)
|
|||
|
||||
MCSymbol *
|
||||
AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
|
||||
return Printer.getSymbol(MO.getGlobal());
|
||||
const GlobalValue *GV = MO.getGlobal();
|
||||
unsigned TargetFlags = MO.getTargetFlags();
|
||||
const Triple &TheTriple = Printer.TM.getTargetTriple();
|
||||
if (!TheTriple.isOSBinFormatCOFF())
|
||||
return Printer.getSymbol(GV);
|
||||
|
||||
assert(TheTriple.isOSWindows() &&
|
||||
"Windows is the only supported COFF target");
|
||||
|
||||
bool IsIndirect = (TargetFlags & AArch64II::MO_DLLIMPORT);
|
||||
if (!IsIndirect)
|
||||
return Printer.getSymbol(GV);
|
||||
|
||||
SmallString<128> Name;
|
||||
Name = "__imp_";
|
||||
Printer.TM.getNameWithPrefix(Name, GV,
|
||||
Printer.getObjFileLowering().getMangler());
|
||||
|
||||
return Ctx.getOrCreateSymbol(Name);
|
||||
}
|
||||
|
||||
MCSymbol *
|
||||
|
|
|
@ -517,7 +517,12 @@ namespace AArch64II {
|
|||
/// thread-local symbol. On Darwin, only one type of thread-local access
|
||||
/// exists (pre linker-relaxation), but on ELF the TLSModel used for the
|
||||
/// referee will affect interpretation.
|
||||
MO_TLS = 0x40
|
||||
MO_TLS = 0x40,
|
||||
|
||||
/// MO_DLLIMPORT - On a symbol operand, this represents that the reference
|
||||
/// to the symbol is for an import stub. This is used for DLL import
|
||||
/// storage class indication on Windows.
|
||||
MO_DLLIMPORT = 0x80,
|
||||
};
|
||||
} // end namespace AArch64II
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
; RUN: llc -mtriple aarch64-unknown-windows-msvc -filetype asm -o - %s | FileCheck %s
|
||||
|
||||
@var = external dllimport global i32
|
||||
@ext = external global i32
|
||||
declare dllimport i32 @external()
|
||||
declare i32 @internal()
|
||||
|
||||
define i32 @get_var() {
|
||||
%1 = load i32, i32* @var, align 4
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
; CHECK-LABEL: get_var
|
||||
; CHECK: adrp x8, __imp_var
|
||||
; CHECK: ldr x8, [x8, __imp_var]
|
||||
; CHECK: ldr w0, [x8]
|
||||
; CHECK: ret
|
||||
|
||||
define i32 @get_ext() {
|
||||
%1 = load i32, i32* @ext, align 4
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
; CHECK-LABEL: get_ext
|
||||
; CHECK: adrp x8, ext
|
||||
; CHECK: ldr w0, [x8, ext]
|
||||
; CHECK: ret
|
||||
|
||||
define i32* @get_var_pointer() {
|
||||
ret i32* @var
|
||||
}
|
||||
|
||||
; CHECK-LABEL: get_var_pointer
|
||||
; CHECK: adrp x0, __imp_var
|
||||
; CHECK: ldr x0, [x0, __imp_var]
|
||||
; CHECK: ret
|
||||
|
||||
define i32 @call_external() {
|
||||
%call = tail call i32 @external()
|
||||
ret i32 %call
|
||||
}
|
||||
|
||||
; CHECK-LABEL: call_external
|
||||
; CHECK: adrp x0, __imp_external
|
||||
; CHECK: ldr x0, [x0, __imp_external]
|
||||
; CHECK: br x0
|
||||
|
||||
define i32 @call_internal() {
|
||||
%call = tail call i32 @internal()
|
||||
ret i32 %call
|
||||
}
|
||||
|
||||
; CHECK-LABEL: call_internal
|
||||
; CHECK: b internal
|
Loading…
Reference in New Issue