[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