Skip to content

[Mips] Handle declspec(dllimport) on mipsel-windows-* triples #120912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 21, 2025

Conversation

hpoussin
Copy link
Contributor

On Windows, imported symbols must be searched with '_imp' prefix.
Support imported global variables and imported functions.

On Windows, imported symbols must be searched with '__imp_' prefix.
Support imported global variables and imported functions.
@llvmbot llvmbot added the mc Machine (object) code label Dec 22, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 22, 2024

@llvm/pr-subscribers-mc

Author: Hervé Poussineau (hpoussin)

Changes

On Windows, imported symbols must be searched with '_imp' prefix.
Support imported global variables and imported functions.


Full diff: https://github.com/llvm/llvm-project/pull/120912.diff

7 Files Affected:

  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h (+6-1)
  • (modified) llvm/lib/Target/Mips/MipsISelLowering.cpp (+16-1)
  • (modified) llvm/lib/Target/Mips/MipsISelLowering.h (+27)
  • (modified) llvm/lib/Target/Mips/MipsMCInstLower.cpp (+12-2)
  • (modified) llvm/lib/Target/Mips/MipsSubtarget.h (+2)
  • (added) llvm/test/CodeGen/Mips/dllimport.ll (+55)
  • (added) llvm/test/MC/Mips/coff-relocs-dllimport.ll (+11)
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
index aa35e7db6bda444..b9a2af3341236a5 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
@@ -92,7 +92,12 @@ namespace MipsII {
     MO_CALL_LO16,
 
     /// Helper operand used to generate R_MIPS_JALR
-    MO_JALR
+    MO_JALR,
+
+    /// MO_DLLIMPORT - On a symbol operand "FOO", this indicates that the
+    /// reference is actually to the "__imp_FOO" symbol.  This is used for
+    /// dllimport linkage on windows.
+    MO_DLLIMPORT = 0x20,
   };
 
   enum {
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index d5f38c414e703d2..469b7a566cea25c 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -2146,6 +2146,14 @@ SDValue MipsTargetLowering::lowerGlobalAddress(SDValue Op,
   GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op);
   const GlobalValue *GV = N->getGlobal();
 
+  if (GV->hasDLLImportStorageClass()) {
+    assert(Subtarget.isTargetWindows() &&
+           "Windows is the only supported COFF target");
+    return getDllimportVariable(
+        N, SDLoc(N), Ty, DAG, DAG.getEntryNode(),
+        MachinePointerInfo::getGOT(DAG.getMachineFunction()));
+  }
+
   if (!isPositionIndependent()) {
     const MipsTargetObjectFile *TLOF =
         static_cast<const MipsTargetObjectFile *>(
@@ -3501,7 +3509,14 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
   }
 
   if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
-    if (IsPIC) {
+    if (Subtarget.isTargetCOFF() &&
+        G->getGlobal()->hasDLLImportStorageClass()) {
+      assert(Subtarget.isTargetWindows() &&
+             "Windows is the only supported COFF target");
+      auto PtrInfo = MachinePointerInfo();
+      Callee = DAG.getLoad(Ty, DL, Chain,
+                           getDllimportSymbol(G, SDLoc(G), Ty, DAG), PtrInfo);
+    } else if (IsPIC) {
       const GlobalValue *Val = G->getGlobal();
       InternalLinkage = Val->hasInternalLinkage();
 
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h
index e245c056de6491e..efd0acef961f616 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.h
+++ b/llvm/lib/Target/Mips/MipsISelLowering.h
@@ -487,6 +487,33 @@ class TargetRegisterClass;
           DAG.getNode(MipsISD::GPRel, DL, DAG.getVTList(Ty), GPRel));
     }
 
+    // This method creates the following nodes, which are necessary for
+    // loading a dllimported symbol:
+    //
+    // (lw (add (shl(%high(sym), 16), %low(sym)))
+    template <class NodeTy>
+    SDValue getDllimportSymbol(NodeTy *N, const SDLoc &DL, EVT Ty,
+                               SelectionDAG &DAG) const {
+      SDValue Hi =
+          getTargetNode(N, Ty, DAG, MipsII::MO_ABS_HI | MipsII::MO_DLLIMPORT);
+      SDValue Lo =
+          getTargetNode(N, Ty, DAG, MipsII::MO_ABS_LO | MipsII::MO_DLLIMPORT);
+      return DAG.getNode(ISD::ADD, DL, Ty, DAG.getNode(MipsISD::Lo, DL, Ty, Lo),
+                         DAG.getNode(MipsISD::Hi, DL, Ty, Hi));
+    }
+
+    // This method creates the following nodes, which are necessary for
+    // loading a dllimported global variable:
+    //
+    // (lw (lw (add (shl(%high(sym), 16), %low(sym))))
+    template <class NodeTy>
+    SDValue getDllimportVariable(NodeTy *N, const SDLoc &DL, EVT Ty,
+                                 SelectionDAG &DAG, SDValue Chain,
+                                 const MachinePointerInfo &PtrInfo) const {
+      return DAG.getLoad(Ty, DL, Chain, getDllimportSymbol(N, DL, Ty, DAG),
+                         PtrInfo);
+    }
+
     /// This function fills Ops, which is the list of operands that will later
     /// be used when a function call node is created. It also generates
     /// copyToReg nodes to set up argument registers.
diff --git a/llvm/lib/Target/Mips/MipsMCInstLower.cpp b/llvm/lib/Target/Mips/MipsMCInstLower.cpp
index b0642f3d1ff283a..e01d0d1e65cf7e9 100644
--- a/llvm/lib/Target/Mips/MipsMCInstLower.cpp
+++ b/llvm/lib/Target/Mips/MipsMCInstLower.cpp
@@ -18,6 +18,7 @@
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -38,8 +39,16 @@ MCOperand MipsMCInstLower::LowerSymbolOperand(const MachineOperand &MO,
   MipsMCExpr::MipsExprKind TargetKind = MipsMCExpr::MEK_None;
   bool IsGpOff = false;
   const MCSymbol *Symbol;
+  SmallString<128> Name;
+  unsigned TargetFlags = MO.getTargetFlags();
 
-  switch(MO.getTargetFlags()) {
+  if (TargetFlags & MipsII::MO_DLLIMPORT) {
+    // Handle dllimport linkage
+    Name += "__imp_";
+    TargetFlags &= ~MipsII::MO_DLLIMPORT;
+  }
+
+  switch (TargetFlags) {
   default:
     llvm_unreachable("Invalid target flag!");
   case MipsII::MO_NO_FLAG:
@@ -125,7 +134,8 @@ MCOperand MipsMCInstLower::LowerSymbolOperand(const MachineOperand &MO,
     break;
 
   case MachineOperand::MO_GlobalAddress:
-    Symbol = AsmPrinter.getSymbol(MO.getGlobal());
+    AsmPrinter.getNameWithPrefix(Name, MO.getGlobal());
+    Symbol = Ctx->getOrCreateSymbol(Name);
     Offset += MO.getOffset();
     break;
 
diff --git a/llvm/lib/Target/Mips/MipsSubtarget.h b/llvm/lib/Target/Mips/MipsSubtarget.h
index c048ab29d5f9b2e..85cf45d4702ae81 100644
--- a/llvm/lib/Target/Mips/MipsSubtarget.h
+++ b/llvm/lib/Target/Mips/MipsSubtarget.h
@@ -301,6 +301,7 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
     return (HasSym32 && isABI_N64()) || isABI_N32() || isABI_O32();
   }
   bool isSingleFloat() const { return IsSingleFloat; }
+  bool isTargetCOFF() const { return TargetTriple.isOSBinFormatCOFF(); }
   bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); }
   bool hasVFPU() const { return HasVFPU; }
   bool inMips16Mode() const { return InMips16Mode; }
@@ -356,6 +357,7 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
   bool os16() const { return Os16; }
 
   bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); }
+  bool isTargetWindows() const { return TargetTriple.isOSWindows(); }
 
   bool isXRaySupported() const override { return true; }
 
diff --git a/llvm/test/CodeGen/Mips/dllimport.ll b/llvm/test/CodeGen/Mips/dllimport.ll
new file mode 100644
index 000000000000000..385199892821e7d
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/dllimport.ll
@@ -0,0 +1,55 @@
+; RUN: llc -mtriple mipsel-windows < %s | FileCheck %s
+
+@Var1 = external dllimport global i32
+@Var2 = available_externally dllimport unnamed_addr constant i32 1
+
+declare dllimport void @fun()
+
+define available_externally dllimport void @inline1() {
+	ret void
+}
+
+define available_externally dllimport void @inline2() alwaysinline {
+	ret void
+}
+
+declare void @dummy(...)
+
+define void @use() nounwind {
+; CHECK:     lui $1, %hi(__imp_fun)
+; CHECK:     addiu $1, $1, %lo(__imp_fun)
+; CHECK:     lw $25, 0($1)
+; CHECK:     jalr $25
+  call void @fun()
+
+; CHECK:     lui $1, %hi(__imp_inline1)
+; CHECK:     addiu $1, $1, %lo(__imp_inline1)
+; CHECK:     lw $25, 0($1)
+; CHECK:     jalr $25
+  call void @inline1()
+
+; CHECK:     lui $1, %hi(__imp_inline2)
+; CHECK:     addiu $1, $1, %lo(__imp_inline2)
+; CHECK:     lw $25, 0($1)
+; CHECK:     jalr $25
+  call void @inline2()
+
+; CHECK:     lui $1, %hi(__imp_Var2)
+; CHECK:     addiu $1, $1, %lo(__imp_Var2)
+; CHECK:     lw $1, 0($1)
+; CHECK:     lw $5, 0($1)
+; CHECK:     lui $1, %hi(__imp_Var1)
+; CHECK:     addiu $1, $1, %lo(__imp_Var1)
+; CHECK:     lw $1, 0($1)
+; CHECK:     lw $4, 0($1)
+  %1 = load i32, ptr @Var1
+  %2 = load i32, ptr @Var2
+  call void(...) @dummy(i32 %1, i32 %2)
+
+  ret void
+}
+
+; CHECK: fp:
+; CHECK-NEXT: .long fun
+@fp = constant ptr @fun
+
diff --git a/llvm/test/MC/Mips/coff-relocs-dllimport.ll b/llvm/test/MC/Mips/coff-relocs-dllimport.ll
new file mode 100644
index 000000000000000..a4d189faefdbcbb
--- /dev/null
+++ b/llvm/test/MC/Mips/coff-relocs-dllimport.ll
@@ -0,0 +1,11 @@
+; RUN: llc -mtriple mipsel-windows -filetype obj < %s | llvm-objdump --reloc - | FileCheck %s
+
+declare dllimport void @fun()
+
+define void @use() nounwind {
+; CHECK: 00000008 IMAGE_REL_MIPS_REFHI     __imp_fun
+; CHECK: 0000000c IMAGE_REL_MIPS_REFLO     __imp_fun
+  call void() @fun()
+
+  ret void
+}

@hpoussin
Copy link
Contributor Author

This is an extract of PR #107744

Copy link

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 0575815b70b486240ace728a33f756cea8fe58fa 3289d9c3b7191456abc642ddc148b643b38c4d02 --extensions h,cpp -- llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h llvm/lib/Target/Mips/MipsISelLowering.cpp llvm/lib/Target/Mips/MipsISelLowering.h llvm/lib/Target/Mips/MipsMCInstLower.cpp llvm/lib/Target/Mips/MipsSubtarget.h
View the diff from clang-format here.
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
index b9a2af3341..11852681be 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsBaseInfo.h
@@ -27,118 +27,118 @@ namespace llvm {
 ///
 namespace MipsII {
   /// Target Operand Flag enum.
-  enum TOF {
-    //===------------------------------------------------------------------===//
-    // Mips Specific MachineOperand flags.
-
-    MO_NO_FLAG,
-
-    /// MO_GOT - Represents the offset into the global offset table at which
-    /// the address the relocation entry symbol resides during execution.
-    MO_GOT,
-
-    /// MO_GOT_CALL - Represents the offset into the global offset table at
-    /// which the address of a call site relocation entry symbol resides
-    /// during execution. This is different from the above since this flag
-    /// can only be present in call instructions.
-    MO_GOT_CALL,
-
-    /// MO_GPREL - Represents the offset from the current gp value to be used
-    /// for the relocatable object file being produced.
-    MO_GPREL,
-
-    /// MO_ABS_HI/LO - Represents the hi or low part of an absolute symbol
-    /// address.
-    MO_ABS_HI,
-    MO_ABS_LO,
-
-    /// MO_TLSGD - Represents the offset into the global offset table at which
-    // the module ID and TSL block offset reside during execution (General
-    // Dynamic TLS).
-    MO_TLSGD,
-
-    /// MO_TLSLDM - Represents the offset into the global offset table at which
-    // the module ID and TSL block offset reside during execution (Local
-    // Dynamic TLS).
-    MO_TLSLDM,
-    MO_DTPREL_HI,
-    MO_DTPREL_LO,
-
-    /// MO_GOTTPREL - Represents the offset from the thread pointer (Initial
-    // Exec TLS).
-    MO_GOTTPREL,
-
-    /// MO_TPREL_HI/LO - Represents the hi and low part of the offset from
-    // the thread pointer (Local Exec TLS).
-    MO_TPREL_HI,
-    MO_TPREL_LO,
-
-    // N32/64 Flags.
-    MO_GPOFF_HI,
-    MO_GPOFF_LO,
-    MO_GOT_DISP,
-    MO_GOT_PAGE,
-    MO_GOT_OFST,
-
-    /// MO_HIGHER/HIGHEST - Represents the highest or higher half word of a
-    /// 64-bit symbol address.
-    MO_HIGHER,
-    MO_HIGHEST,
-
-    /// MO_GOT_HI16/LO16, MO_CALL_HI16/LO16 - Relocations used for large GOTs.
-    MO_GOT_HI16,
-    MO_GOT_LO16,
-    MO_CALL_HI16,
-    MO_CALL_LO16,
-
-    /// Helper operand used to generate R_MIPS_JALR
-    MO_JALR,
-
-    /// MO_DLLIMPORT - On a symbol operand "FOO", this indicates that the
-    /// reference is actually to the "__imp_FOO" symbol.  This is used for
-    /// dllimport linkage on windows.
-    MO_DLLIMPORT = 0x20,
-  };
-
-  enum {
-    //===------------------------------------------------------------------===//
-    // Instruction encodings.  These are the standard/most common forms for
-    // Mips instructions.
-    //
-
-    // Pseudo - This represents an instruction that is a pseudo instruction
-    // or one that has not been implemented yet.  It is illegal to code generate
-    // it, but tolerated for intermediate implementation stages.
-    Pseudo   = 0,
-
-    /// FrmR - This form is for instructions of the format R.
-    FrmR  = 1,
-    /// FrmI - This form is for instructions of the format I.
-    FrmI  = 2,
-    /// FrmJ - This form is for instructions of the format J.
-    FrmJ  = 3,
-    /// FrmFR - This form is for instructions of the format FR.
-    FrmFR = 4,
-    /// FrmFI - This form is for instructions of the format FI.
-    FrmFI = 5,
-    /// FrmOther - This form is for instructions that have no specific format.
-    FrmOther = 6,
-
-    FormMask = 15,
-    /// IsCTI - Instruction is a Control Transfer Instruction.
-    IsCTI = 1 << 4,
-    /// HasForbiddenSlot - Instruction has a forbidden slot.
-    HasForbiddenSlot = 1 << 5,
-    /// HasFCCRegOperand - Instruction uses an $fcc<x> register.
-    HasFCCRegOperand = 1 << 6
-
-  };
-
-  enum OperandType : unsigned {
-    OPERAND_FIRST_MIPS_MEM_IMM = MCOI::OPERAND_FIRST_TARGET,
-    OPERAND_MEM_SIMM9 = OPERAND_FIRST_MIPS_MEM_IMM,
-    OPERAND_LAST_MIPS_MEM_IMM = OPERAND_MEM_SIMM9
-  };
+enum TOF {
+  //===------------------------------------------------------------------===//
+  // Mips Specific MachineOperand flags.
+
+  MO_NO_FLAG,
+
+  /// MO_GOT - Represents the offset into the global offset table at which
+  /// the address the relocation entry symbol resides during execution.
+  MO_GOT,
+
+  /// MO_GOT_CALL - Represents the offset into the global offset table at
+  /// which the address of a call site relocation entry symbol resides
+  /// during execution. This is different from the above since this flag
+  /// can only be present in call instructions.
+  MO_GOT_CALL,
+
+  /// MO_GPREL - Represents the offset from the current gp value to be used
+  /// for the relocatable object file being produced.
+  MO_GPREL,
+
+  /// MO_ABS_HI/LO - Represents the hi or low part of an absolute symbol
+  /// address.
+  MO_ABS_HI,
+  MO_ABS_LO,
+
+  /// MO_TLSGD - Represents the offset into the global offset table at which
+  // the module ID and TSL block offset reside during execution (General
+  // Dynamic TLS).
+  MO_TLSGD,
+
+  /// MO_TLSLDM - Represents the offset into the global offset table at which
+  // the module ID and TSL block offset reside during execution (Local
+  // Dynamic TLS).
+  MO_TLSLDM,
+  MO_DTPREL_HI,
+  MO_DTPREL_LO,
+
+  /// MO_GOTTPREL - Represents the offset from the thread pointer (Initial
+  // Exec TLS).
+  MO_GOTTPREL,
+
+  /// MO_TPREL_HI/LO - Represents the hi and low part of the offset from
+  // the thread pointer (Local Exec TLS).
+  MO_TPREL_HI,
+  MO_TPREL_LO,
+
+  // N32/64 Flags.
+  MO_GPOFF_HI,
+  MO_GPOFF_LO,
+  MO_GOT_DISP,
+  MO_GOT_PAGE,
+  MO_GOT_OFST,
+
+  /// MO_HIGHER/HIGHEST - Represents the highest or higher half word of a
+  /// 64-bit symbol address.
+  MO_HIGHER,
+  MO_HIGHEST,
+
+  /// MO_GOT_HI16/LO16, MO_CALL_HI16/LO16 - Relocations used for large GOTs.
+  MO_GOT_HI16,
+  MO_GOT_LO16,
+  MO_CALL_HI16,
+  MO_CALL_LO16,
+
+  /// Helper operand used to generate R_MIPS_JALR
+  MO_JALR,
+
+  /// MO_DLLIMPORT - On a symbol operand "FOO", this indicates that the
+  /// reference is actually to the "__imp_FOO" symbol.  This is used for
+  /// dllimport linkage on windows.
+  MO_DLLIMPORT = 0x20,
+};
+
+enum {
+  //===------------------------------------------------------------------===//
+  // Instruction encodings.  These are the standard/most common forms for
+  // Mips instructions.
+  //
+
+  // Pseudo - This represents an instruction that is a pseudo instruction
+  // or one that has not been implemented yet.  It is illegal to code generate
+  // it, but tolerated for intermediate implementation stages.
+  Pseudo = 0,
+
+  /// FrmR - This form is for instructions of the format R.
+  FrmR = 1,
+  /// FrmI - This form is for instructions of the format I.
+  FrmI = 2,
+  /// FrmJ - This form is for instructions of the format J.
+  FrmJ = 3,
+  /// FrmFR - This form is for instructions of the format FR.
+  FrmFR = 4,
+  /// FrmFI - This form is for instructions of the format FI.
+  FrmFI = 5,
+  /// FrmOther - This form is for instructions that have no specific format.
+  FrmOther = 6,
+
+  FormMask = 15,
+  /// IsCTI - Instruction is a Control Transfer Instruction.
+  IsCTI = 1 << 4,
+  /// HasForbiddenSlot - Instruction has a forbidden slot.
+  HasForbiddenSlot = 1 << 5,
+  /// HasFCCRegOperand - Instruction uses an $fcc<x> register.
+  HasFCCRegOperand = 1 << 6
+
+};
+
+enum OperandType : unsigned {
+  OPERAND_FIRST_MIPS_MEM_IMM = MCOI::OPERAND_FIRST_TARGET,
+  OPERAND_MEM_SIMM9 = OPERAND_FIRST_MIPS_MEM_IMM,
+  OPERAND_LAST_MIPS_MEM_IMM = OPERAND_MEM_SIMM9
+};
 }
 
 inline static MCRegister getMSARegFromFReg(MCRegister Reg) {

@hpoussin
Copy link
Contributor Author

hpoussin commented Dec 22, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️
You can test this locally with the following command:

View the diff from clang-format here.

I didn't follow clang-format, because it seems unnecessary to reindent the whole TOF enum just to add one member.
Also I don't see why I need to reindent the OperandType enum, which I didn't change.
Maybe because they are located in the MipsII namespace ?

However, if it is required, I can do it.

@hpoussin
Copy link
Contributor Author

Ping

1 similar comment
@hpoussin
Copy link
Contributor Author

hpoussin commented Jan 5, 2025

Ping

@mstorsjo mstorsjo requested a review from wzssyqa January 5, 2025 20:56
@hpoussin
Copy link
Contributor Author

Ping

@hpoussin
Copy link
Contributor Author

Ping. Maybe @wzssyqa or @FlyGoat ?
And any comment on the formatting?

@wzssyqa wzssyqa merged commit 26b87aa into llvm:main Jan 21, 2025
9 of 10 checks passed
@hpoussin hpoussin deleted the clang-mips-coff-10 branch January 21, 2025 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants