CodeGen: handle llvm.used properly for COFF
authorSaleem Abdulrasool <compnerd@compnerd.org>
Sat, 20 Jan 2018 00:28:02 +0000 (00:28 +0000)
committerSaleem Abdulrasool <compnerd@compnerd.org>
Sat, 20 Jan 2018 00:28:02 +0000 (00:28 +0000)
`llvm.used` contains a list of pointers to named values which the
compiler, assembler, and linker are required to treat as if there is a
reference that they cannot see.  Ensure that the symbols are preserved
by adding an explicit `-include` reference to the linker command.

llvm-svn: 323017

llvm/include/llvm/CodeGen/TargetLoweringObjectFile.h
llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
llvm/include/llvm/IR/Mangler.h
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
llvm/lib/IR/Mangler.cpp
llvm/test/CodeGen/X86/coff-no-dead-strip.ll [new file with mode: 0644]

index fe77c29..9877072 100644 (file)
@@ -183,6 +183,9 @@ public:
   virtual void emitLinkerFlagsForGlobal(raw_ostream &OS,
                                         const GlobalValue *GV) const {}
 
+  virtual void emitLinkerFlagsForUsed(raw_ostream &OS,
+                                      const GlobalValue *GV) const {}
+
 protected:
   virtual MCSection *SelectSectionForGlobal(const GlobalObject *GO,
                                             SectionKind Kind,
index 69de9f8..8ccb51c 100644 (file)
@@ -163,6 +163,9 @@ public:
 
   void emitLinkerFlagsForGlobal(raw_ostream &OS,
                                 const GlobalValue *GV) const override;
+
+  void emitLinkerFlagsForUsed(raw_ostream &OS,
+                              const GlobalValue *GV) const override;
 };
 
 class TargetLoweringObjectFileWasm : public TargetLoweringObjectFile {
index 56ee213..0261c00 100644 (file)
@@ -50,6 +50,9 @@ public:
 void emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
                                   const Triple &TT, Mangler &Mangler);
 
+void emitLinkerFlagsForUsedCOFF(raw_ostream &OS, const GlobalValue *GV,
+                                const Triple &T, Mangler &M);
+
 } // End llvm namespace
 
 #endif
index 2a20c64..87d9137 100644 (file)
@@ -1434,6 +1434,7 @@ bool AsmPrinter::doFinalization(Module &M) {
     // Emit /EXPORT: flags for each exported global as necessary.
     const auto &TLOF = getObjFileLowering();
     std::string Flags;
+
     for (const GlobalValue &GV : M.global_values()) {
       raw_string_ostream OS(Flags);
       TLOF.emitLinkerFlagsForGlobal(OS, &GV);
@@ -1444,6 +1445,35 @@ bool AsmPrinter::doFinalization(Module &M) {
       }
       Flags.clear();
     }
+
+    // Emit /INCLUDE: flags for each used global as necessary.
+    if (const auto *LU = M.getNamedGlobal("llvm.used")) {
+      assert(LU->hasInitializer() &&
+             "expected llvm.used to have an initializer");
+      assert(isa<ArrayType>(LU->getValueType()) &&
+             "expected llvm.used to be an array type");
+      if (const auto *A = cast<ConstantArray>(LU->getInitializer())) {
+        for (const Value *Op : A->operands()) {
+          const auto *GV =
+              cast<GlobalValue>(Op->stripPointerCastsNoFollowAliases());
+          // Global symbols with internal linkage are not visible to the linker,
+          // and thus would cause an error when the linker tried to preserve the
+          // symbol due to the `/include:` directive.
+          if (GV->hasInternalLinkage())
+            continue;
+
+          raw_string_ostream OS(Flags);
+          TLOF.emitLinkerFlagsForUsed(OS, GV);
+          OS.flush();
+
+          if (!Flags.empty()) {
+            OutStreamer->SwitchSection(TLOF.getDrectveSection());
+            OutStreamer->EmitBytes(Flags);
+          }
+          Flags.clear();
+        }
+      }
+    }
   }
 
   // Allow the target to emit any magic that it wants at the end of the file,
index bdc4bc0..0e90df9 100644 (file)
@@ -1250,6 +1250,11 @@ void TargetLoweringObjectFileCOFF::emitLinkerFlagsForGlobal(
   emitLinkerFlagsForGlobalCOFF(OS, GV, getTargetTriple(), getMangler());
 }
 
+void TargetLoweringObjectFileCOFF::emitLinkerFlagsForUsed(
+    raw_ostream &OS, const GlobalValue *GV) const {
+  emitLinkerFlagsForUsedCOFF(OS, GV, getTargetTriple(), getMangler());
+}
+
 //===----------------------------------------------------------------------===//
 //                                  Wasm
 //===----------------------------------------------------------------------===//
index 03723bf..7adcc59 100644 (file)
@@ -204,3 +204,13 @@ void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
       OS << ",data";
   }
 }
+
+void llvm::emitLinkerFlagsForUsedCOFF(raw_ostream &OS, const GlobalValue *GV,
+                                      const Triple &T, Mangler &M) {
+  if (!T.isKnownWindowsMSVCEnvironment())
+    return;
+
+  OS << " /INCLUDE:";
+  M.getNameWithPrefix(OS, GV, false);
+}
+
diff --git a/llvm/test/CodeGen/X86/coff-no-dead-strip.ll b/llvm/test/CodeGen/X86/coff-no-dead-strip.ll
new file mode 100644 (file)
index 0000000..adac90d
--- /dev/null
@@ -0,0 +1,20 @@
+; RUN: llc -mtriple i686-windows-msvc -filetype asm -o - %s | FileCheck %s -check-prefix CHECK -check-prefix CHECK-ULP
+; RUN: llc -mtriple x86_64-windows-msvc -filetype asm -o - %s | FileCheck %s -check-prefix CHECK -check-prefix CHECK-NOULP
+; RUN: llc -mtriple thumbv7-windows-msvc -filetype asm -o - %s | FileCheck %s -check-prefix CHECK -check-prefix CHECK-NOULP
+
+@i = global i32 0
+@j = weak global i32 0
+@k = internal global i32 0
+declare x86_vectorcallcc void @l()
+
+@llvm.used = appending global [4 x i8*] [i8* bitcast (i32* @i to i8*), i8* bitcast (i32* @j to i8*), i8* bitcast (i32* @k to i8*), i8* bitcast (void ()* @l to i8*)]
+
+; CHECK: .section .drectve
+; CHECK-ULP: .ascii " /INCLUDE:_i"
+; CHECK-ULP: .ascii " /INCLUDE:_j"
+; CHECK-ULP-NOT: .ascii " /INCLUDE:_k"
+; CHECK-NOULP: .ascii " /INCLUDE:i"
+; CHECK-NOULP: .ascii " /INCLUDE:j"
+; CHECK-NOULP-NOT: .ascii " /INCLUDE:k"
+; CHECK: .ascii " /INCLUDE:l@@0"
+