[lld] [lld][COFF][LTO] Implement /opt:emitasm option (PR #67079)

Matheus Izvekov via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 27 15:11:58 PDT 2023


https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/67079

>From b5e750e9df628834cdd0d85f12e744649d037ff0 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 21 Sep 2023 02:12:32 +0200
Subject: [PATCH 1/2] [lld][COFF][LTO] Implement /opt:emitllvm option

With this new option, bitcode will be emited instead of object code.
This is analogous to the `--plugin-opt=emit-llvm` option in the ELF
linker.
---
 lld/COFF/Config.h              |  1 +
 lld/COFF/Driver.cpp            |  5 ++++-
 lld/COFF/LTO.cpp               |  9 +++++++++
 lld/test/COFF/lto-emit-llvm.ll | 14 ++++++++++++++
 4 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lld/test/COFF/lto-emit-llvm.ll

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 4ade2c953c73e40..ee6829c3d225017 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -311,6 +311,7 @@ struct Configuration {
   bool pseudoRelocs = false;
   bool stdcallFixup = false;
   bool writeCheckSum = false;
+  bool emitLLVM = false;
 };
 
 } // namespace lld::coff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 61a04a74aa60278..03a6c62bbd75678 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1823,6 +1823,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
         ltoDebugPM = true;
       } else if (s == "noltodebugpassmanager") {
         ltoDebugPM = false;
+      } else if (s == "emitllvm") {
+        config->emitLLVM = true;
       } else if (s.consume_front("lldlto=")) {
         if (s.getAsInteger(10, config->ltoo) || config->ltoo > 3)
           error("/opt:lldlto: invalid optimization level: " + s);
@@ -2395,7 +2397,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // If -thinlto-index-only is given, we should create only "index
   // files" and not object files. Index file creation is already done
   // in addCombinedLTOObject, so we are done if that's the case.
-  if (config->thinLTOIndexOnly)
+  // Likewise, for /opt:emitllvm we only emit bitcode.
+  if (config->emitLLVM || config->thinLTOIndexOnly)
     return;
 
   // If we generated native object files from bitcode files, this resolves
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 6ca000b466a1264..ee9cae0852d5345 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -87,6 +87,15 @@ lto::Config BitcodeCompiler::createConfig() {
   c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate;
   c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch;
 
+  if (ctx.config.emitLLVM) {
+    c.PostInternalizeModuleHook = [this](size_t task, const Module &m) {
+      if (std::unique_ptr<raw_fd_ostream> os =
+              openLTOOutputFile(ctx.config.outputFile))
+        WriteBitcodeToFile(m, *os, false);
+      return false;
+    };
+  }
+
   if (ctx.config.saveTemps)
     checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
                               /*UseInputModulePath*/ true));
diff --git a/lld/test/COFF/lto-emit-llvm.ll b/lld/test/COFF/lto-emit-llvm.ll
new file mode 100644
index 000000000000000..d7ab61d043a0099
--- /dev/null
+++ b/lld/test/COFF/lto-emit-llvm.ll
@@ -0,0 +1,14 @@
+; REQUIRES: x86
+; RUN: llvm-as -o %T/lto.obj %s
+
+; RUN: lld-link /opt:emitllvm /out:%T/lto.bc /entry:main /subsystem:console %T/lto.obj
+; RUN: llvm-dis %T/lto.bc -o - | FileCheck %s
+
+; CHECK: define void @main()
+
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+define void @main() {
+  ret void
+}

>From accd9d40cc60db0d7c5ed4335b405430bb683fab Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Fri, 22 Sep 2023 01:48:17 +0200
Subject: [PATCH 2/2] [lld][COFF][LTO] Implement /opt:emitasm option

With this new option, assembly code will be emited instead of object code.
This is analogous to the `--lto-emit-asm` option in the ELF linker.
---
 lld/COFF/Config.h             |  1 +
 lld/COFF/Driver.cpp           |  6 ++++--
 lld/COFF/LTO.cpp              | 17 ++++++++++++-----
 lld/test/COFF/lto-emit-asm.ll | 28 ++++++++++++++++++++++++++++
 lld/test/COFF/pdb-thinlto.ll  |  8 ++++----
 5 files changed, 49 insertions(+), 11 deletions(-)
 create mode 100644 lld/test/COFF/lto-emit-asm.ll

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index ee6829c3d225017..c80df08cfcf09aa 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -312,6 +312,7 @@ struct Configuration {
   bool stdcallFixup = false;
   bool writeCheckSum = false;
   bool emitLLVM = false;
+  bool emitASM = false;
 };
 
 } // namespace lld::coff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 03a6c62bbd75678..304344512cf61a0 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1825,6 +1825,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
         ltoDebugPM = false;
       } else if (s == "emitllvm") {
         config->emitLLVM = true;
+      } else if (s == "emitasm") {
+        config->emitASM = true;
       } else if (s.consume_front("lldlto=")) {
         if (s.getAsInteger(10, config->ltoo) || config->ltoo > 3)
           error("/opt:lldlto: invalid optimization level: " + s);
@@ -2397,8 +2399,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // If -thinlto-index-only is given, we should create only "index
   // files" and not object files. Index file creation is already done
   // in addCombinedLTOObject, so we are done if that's the case.
-  // Likewise, for /opt:emitllvm we only emit bitcode.
-  if (config->emitLLVM || config->thinLTOIndexOnly)
+  // Likewise, for /opt:emitllvm and /opt:emitasm we only emit bitcode / asm.
+  if (config->emitLLVM || config->emitASM || config->thinLTOIndexOnly)
     return;
 
   // If we generated native object files from bitcode files, this resolves
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index ee9cae0852d5345..ae207abc801437d 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -96,6 +96,11 @@ lto::Config BitcodeCompiler::createConfig() {
     };
   }
 
+  if (ctx.config.emitASM) {
+    c.CGFileType = CodeGenFileType::AssemblyFile;
+    c.Options.MCOptions.AsmVerbose = true;
+  }
+
   if (ctx.config.saveTemps)
     checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
                               /*UseInputModulePath*/ true));
@@ -213,6 +218,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
     pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files);
 
   std::vector<InputFile *> ret;
+  const char *Ext = ctx.config.emitASM ? ".asm" : ".obj";
   for (unsigned i = 0; i != maxTasks; ++i) {
     StringRef bitcodeFilePath;
     // Get the native object contents either from the cache or from memory.  Do
@@ -235,20 +241,21 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
     if (bitcodeFilePath == "ld-temp.o") {
       ltoObjName =
           saver().save(Twine(ctx.config.outputFile) + ".lto" +
-                       (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+                       (i == 0 ? Twine("") : Twine('.') + Twine(i)) + Ext);
     } else {
       StringRef directory = sys::path::parent_path(bitcodeFilePath);
-      StringRef baseName = sys::path::filename(bitcodeFilePath);
+      StringRef baseName = sys::path::stem(bitcodeFilePath);
       StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile);
       SmallString<64> path;
       sys::path::append(path, directory,
-                        outputFileBaseName + ".lto." + baseName);
+                        outputFileBaseName + ".lto." + baseName + Ext);
       sys::path::remove_dots(path, true);
       ltoObjName = saver().save(path.str());
     }
-    if (ctx.config.saveTemps)
+    if (ctx.config.saveTemps || ctx.config.emitASM)
       saveBuffer(buf[i].second, ltoObjName);
-    ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
+    if (!ctx.config.emitASM)
+      ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
   }
 
   return ret;
diff --git a/lld/test/COFF/lto-emit-asm.ll b/lld/test/COFF/lto-emit-asm.ll
new file mode 100644
index 000000000000000..52199ebe3a569c6
--- /dev/null
+++ b/lld/test/COFF/lto-emit-asm.ll
@@ -0,0 +1,28 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.obj
+
+; RUN: lld-link /opt:emitasm /dll /noentry /include:f1 /include:f2 %t.obj /opt:lldltopartitions=1 /out:%t.1p /lldsavetemps
+; RUN: cat %t.1p.lto.asm | FileCheck %s
+; RUN: llvm-dis %t.1p.0.4.opt.bc -o - | FileCheck --check-prefix=OPT %s
+
+; RUN: lld-link /opt:emitasm /dll /noentry /include:f1 /include:f2 %t.obj /opt:lldltopartitions=2 /out:%t.2p
+; RUN: cat %t.2p.lto.asm %t.2p.lto.1.asm | FileCheck %s
+
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+;; Note: we also check for the presence of comments; /opt:emitasm output should be verbose.
+
+; CHECK-DAG: # -- Begin function f1
+; CHECK-DAG: f1:
+; OPT: define void @f1()
+define void @f1() {
+  ret void
+}
+
+; CHECK-DAG: # -- Begin function f2
+; CHECK-DAG: f2:
+; OPT: define void @f2()
+define void @f2() {
+  ret void
+}
diff --git a/lld/test/COFF/pdb-thinlto.ll b/lld/test/COFF/pdb-thinlto.ll
index 6ea0f28e1ef1e77..10559e2191650e4 100644
--- a/lld/test/COFF/pdb-thinlto.ll
+++ b/lld/test/COFF/pdb-thinlto.ll
@@ -29,8 +29,8 @@ declare void @foo()
 
 ; CHECK:                           Modules
 ; CHECK: ============================================================
-; CHECK: Mod 0000 | `{{.*}}main.exe.lto.main.bc`:
-; CHECK: Obj: `{{.*}}main.exe.lto.main.bc`:
-; CHECK: Mod 0001 | `{{.*}}main.exe.lto.foo.bc`:
-; CHECK: Obj: `{{.*}}main.exe.lto.foo.bc`:
+; CHECK: Mod 0000 | `{{.*}}main.exe.lto.main.obj`:
+; CHECK: Obj: `{{.*}}main.exe.lto.main.obj`:
+; CHECK: Mod 0001 | `{{.*}}main.exe.lto.foo.obj`:
+; CHECK: Obj: `{{.*}}main.exe.lto.foo.obj`:
 ; CHECK: Mod 0002 | `* Linker *`:



More information about the llvm-commits mailing list