[llvm] a6ac968 - [Arm64EC] Refer to dllimport'ed functions correctly.
Eli Friedman via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 20 15:09:57 PDT 2022
Author: Eli Friedman
Date: 2022-10-20T15:08:56-07:00
New Revision: a6ac968360183013affa8cb372bd5b80cba00ac9
URL: https://github.com/llvm/llvm-project/commit/a6ac968360183013affa8cb372bd5b80cba00ac9
DIFF: https://github.com/llvm/llvm-project/commit/a6ac968360183013affa8cb372bd5b80cba00ac9.diff
LOG: [Arm64EC] Refer to dllimport'ed functions correctly.
Arm64EC has two different ways to refer to dllimport'ed functions in an
object file. One is using the usual __imp_ prefix, the other is using an
Arm64EC-specific prefix __imp_aux_. As far as I can tell, if a function
is in an x64 DLL, __imp_aux_ refers to the actual x64 address, while
__imp_ points to some linker-generated code that calls the exit thunk.
So __imp_aux_ is used to refer to the address in non-call contexts,
while __imp_ is used for calls to avoid the indirect call checker.
There's one twist to this, though: if an object refers to a symbol using
the __imp_aux_ prefix, the object file's symbol table must also contain
the symbol with the usual __imp_ prefix. The symbol doesn't actually
have to be used anywhere, it just has to exist; otherwise, the linker's
symbol lookup in x64 import libraries doesn't work correctly. Currently,
this is handled by emitting a .globl __imp_foo directive; we could try
to design some better way to handle this.
One minor quirk I haven't figured out: apparently, in Arm64EC mode, MSVC
prefers to use a linker-synthesized stub to call dllimport'ed functions,
instead of branching directly. The linker stub appears to do the same
thing that inline code would do, so not sure if it's just a code-size
optimization, or if the synthesized stub can actually do something other
than just load from the import table in some circumstances.
Differential Revision: https://reviews.llvm.org/D136202
Added:
llvm/test/CodeGen/AArch64/arm64ec-dllimport.ll
Modified:
llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
llvm/lib/Target/AArch64/AArch64MCInstLower.cpp
llvm/lib/Target/AArch64/AArch64Subtarget.cpp
llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
Removed:
################################################################################
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index fba1da74e810f..943a489f0fa28 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -7716,7 +7716,8 @@ SDValue AArch64TargetLowering::LowerGlobalAddress(SDValue Op,
}
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
index adc03b7656bd3..f4d9186489a00 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -6880,6 +6880,7 @@ AArch64InstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const {
{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
index 450c270fcc9b5..69a66f4335c1c 100644
--- 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 @@ AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
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
index 9a78cbd45cbd0..5b3b6c00ed216 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
@@ -347,8 +347,11 @@ AArch64Subtarget::ClassifyGlobalReference(const GlobalValue *GV,
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 @@ unsigned AArch64Subtarget::classifyGlobalFunctionReference(
!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
index be08a3a1a67e3..94bf55e2d4d2b 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -783,6 +783,13 @@ namespace AArch64II {
/// 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
index 0000000000000..c1350d55f72e7
--- /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
+
+ at 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
+}
More information about the llvm-commits
mailing list