From 373c8efa1e2e35324878b83027e20b3970876e1a Mon Sep 17 00:00:00 2001 From: Martin Storsjo Date: Wed, 25 Oct 2017 07:25:18 +0000 Subject: [PATCH] [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 --- .../Target/AArch64/AArch64ISelLowering.cpp | 46 ++++++++++------ llvm/lib/Target/AArch64/AArch64ISelLowering.h | 8 +-- .../lib/Target/AArch64/AArch64MCInstLower.cpp | 22 +++++++- .../Target/AArch64/Utils/AArch64BaseInfo.h | 7 ++- llvm/test/CodeGen/AArch64/dllimport.ll | 54 +++++++++++++++++++ 5 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/dllimport.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index ea63919474cc..bec872ae8c09 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -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 -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 -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 -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(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 diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index f4e08ad165e4..dfeeabf642c5 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -538,10 +538,12 @@ private: unsigned Flag) const; SDValue getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; - template SDValue getGOT(NodeTy *N, SelectionDAG &DAG) const; template - SDValue getAddrLarge(NodeTy *N, SelectionDAG &DAG) const; - template SDValue getAddr(NodeTy *N, SelectionDAG &DAG) const; + SDValue getGOT(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; + template + SDValue getAddrLarge(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const; + template + 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; diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp index f82b9dbc2c9f..f1281a1b9124 100644 --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -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 * diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h index 5d76681cd97b..c1c799b7b349 100644 --- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h +++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h @@ -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 diff --git a/llvm/test/CodeGen/AArch64/dllimport.ll b/llvm/test/CodeGen/AArch64/dllimport.ll new file mode 100644 index 000000000000..fad049a54cd2 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/dllimport.ll @@ -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