diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -7716,7 +7716,8 @@ } EVT PtrVT = getPointerTy(DAG.getDataLayout()); SDLoc DL(GN); - if (OpFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB)) + if (OpFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_DLLIMPORTAUX | + AArch64II::MO_COFFSTUB)) Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, MachinePointerInfo::getGOT(DAG.getMachineFunction())); return Result; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -6880,6 +6880,7 @@ {MO_S, "aarch64-s"}, {MO_TLS, "aarch64-tls"}, {MO_DLLIMPORT, "aarch64-dllimport"}, + {MO_DLLIMPORTAUX, "aarch64-dllimportaux"}, {MO_PREL, "aarch64-prel"}, {MO_TAGGED, "aarch64-tagged"}}; return makeArrayRef(TargetFlags); diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -22,6 +22,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/CommandLine.h" #include "llvm/Target/TargetLoweringObjectFile.h" @@ -44,15 +45,38 @@ assert(TheTriple.isOSWindows() && "Windows is the only supported COFF target"); - bool IsIndirect = (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB)); + bool IsIndirect = + (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_DLLIMPORTAUX | + AArch64II::MO_COFFSTUB)); if (!IsIndirect) return Printer.getSymbol(GV); SmallString<128> Name; - if (TargetFlags & AArch64II::MO_DLLIMPORT) + + if (TargetFlags & AArch64II::MO_DLLIMPORTAUX) { + // __imp_aux is specific to arm64EC; it represents the actual address of + // an imported function without any thunks. + // + // If we see a reference to an "aux" symbol, also emit a reference to the + // corresponding non-aux symbol. Otherwise, the Microsoft linker behaves + // strangely when linking against x64 import libararies. + // + // emitSymbolAttribute() doesn't have any real effect here; it just + // ensures the symbol name appears in the assembly without any + // side-effects. It might make sense to design a cleaner way to express + // this. + Name = "__imp_"; + Printer.TM.getNameWithPrefix(Name, GV, + Printer.getObjFileLowering().getMangler()); + MCSymbol *ExtraSym = Ctx.getOrCreateSymbol(Name); + Printer.OutStreamer->emitSymbolAttribute(ExtraSym, MCSA_Global); + + Name = "__imp_aux_"; + } else if (TargetFlags & AArch64II::MO_DLLIMPORT) { Name = "__imp_"; - else if (TargetFlags & AArch64II::MO_COFFSTUB) + } else if (TargetFlags & AArch64II::MO_COFFSTUB) { Name = ".refptr."; + } Printer.TM.getNameWithPrefix(Name, GV, Printer.getObjFileLowering().getMangler()); diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -347,8 +347,11 @@ return AArch64II::MO_GOT; if (!TM.shouldAssumeDSOLocal(*GV->getParent(), GV)) { - if (GV->hasDLLImportStorageClass()) + if (GV->hasDLLImportStorageClass()) { + if (isWindowsArm64EC() && GV->getValueType()->isFunctionTy()) + return AArch64II::MO_GOT | AArch64II::MO_DLLIMPORTAUX; return AArch64II::MO_GOT | AArch64II::MO_DLLIMPORT; + } if (getTargetTriple().isOSWindows()) return AArch64II::MO_GOT | AArch64II::MO_COFFSTUB; return AArch64II::MO_GOT; @@ -385,9 +388,17 @@ !TM.shouldAssumeDSOLocal(*GV->getParent(), GV)) return AArch64II::MO_GOT; - // Use ClassifyGlobalReference for setting MO_DLLIMPORT/MO_COFFSTUB. - if (getTargetTriple().isOSWindows()) + if (getTargetTriple().isOSWindows()) { + if (isWindowsArm64EC() && GV->getValueType()->isFunctionTy() && + GV->hasDLLImportStorageClass()) { + // On Arm64EC, if we're calling a function directly, use MO_DLLIMPORT, + // not MO_DLLIMPORTAUX. + return AArch64II::MO_GOT | AArch64II::MO_DLLIMPORT; + } + + // Use ClassifyGlobalReference for setting MO_DLLIMPORT/MO_COFFSTUB. return ClassifyGlobalReference(GV, TM); + } return AArch64II::MO_NO_FLAG; } diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h --- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h +++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h @@ -783,6 +783,13 @@ /// SP-relative load or store instruction (which do not check tags), or to /// an LDG instruction to obtain the tag value. MO_TAGGED = 0x400, + + /// MO_DLLIMPORTAUX - Symbol refers to "auxilliary" import stub. On + /// Arm64EC, there are two kinds of import stubs used for DLL import of + /// functions: MO_DLLIMPORT refers to natively callable Arm64 code, and + /// MO_DLLIMPORTAUX refers to the original address which can be compared + /// for equality. + MO_DLLIMPORTAUX = 0x800, }; } // end namespace AArch64II diff --git a/llvm/test/CodeGen/AArch64/arm64ec-dllimport.ll b/llvm/test/CodeGen/AArch64/arm64ec-dllimport.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64ec-dllimport.ll @@ -0,0 +1,38 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=arm64ec-pc-windows-msvc < %s | FileCheck %s + +@a = external dllimport global i32 +declare dllimport void @b() + +define ptr @dllimport_var() nounwind { +; CHECK-LABEL: dllimport_var: +; CHECK: // %bb.0: +; CHECK-NEXT: adrp x0, __imp_a +; CHECK-NEXT: ldr x0, [x0, :lo12:__imp_a] +; CHECK-NEXT: ret + ret ptr @a +} + +define ptr @dllimport_fn() nounwind { +; CHECK-LABEL: dllimport_fn: +; CHECK: // %bb.0: +; CHECK-NEXT: .globl __imp_b +; CHECK-NEXT: adrp x0, __imp_aux_b +; CHECK-NEXT: .globl __imp_b +; CHECK-NEXT: ldr x0, [x0, :lo12:__imp_aux_b] +; CHECK-NEXT: ret + ret ptr @b +} + +define void @dllimport_fn_call() nounwind { +; CHECK-LABEL: dllimport_fn_call: +; CHECK: // %bb.0: +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: adrp x8, __imp_b +; CHECK-NEXT: ldr x8, [x8, :lo12:__imp_b] +; CHECK-NEXT: blr x8 +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ret + call void @b() + ret void +}