Skip to content

[lld][COFF][LTO] Implement /lldemit:llvm option #66964

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
Oct 2, 2023

Conversation

mizvekov
Copy link
Contributor

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.

@llvmbot
Copy link
Member

llvmbot commented Sep 21, 2023

@llvm/pr-subscribers-lld-coff
@llvm/pr-subscribers-lld
@llvm/pr-subscribers-lld-macho
@llvm/pr-subscribers-lld-wasm
@llvm/pr-subscribers-platform-windows

@llvm/pr-subscribers-lld-elf

Changes

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.


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

8 Files Affected:

  • (modified) lld/COFF/Config.h (+1)
  • (modified) lld/COFF/Driver.cpp (+5-2)
  • (modified) lld/COFF/LTO.cpp (+10-12)
  • (modified) lld/Common/Filesystem.cpp (+27-1)
  • (modified) lld/ELF/LTO.cpp (+1-26)
  • (modified) lld/MachO/LTO.cpp (+1-13)
  • (modified) lld/include/lld/Common/Filesystem.h (+4)
  • (added) lld/test/COFF/lto-emit-llvm.ll (+14)
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 d7476e91e03e384..8b3bfb4bed3cdb8 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1822,6 +1822,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);
@@ -2101,7 +2103,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Handle /RELEASE
   if (args.hasArg(OPT_release))
     config->writeCheckSum = true;
-  
+
   // Handle /safeseh, x86 only, on by default, except for mingw.
   if (config->machine == I386) {
     config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
@@ -2393,7 +2395,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 /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 c185da5370fa3dd..ee9cae0852d5345 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -13,6 +13,7 @@
 #include "Symbols.h"
 #include "lld/Common/Args.h"
 #include "lld/Common/CommonLinkerContext.h"
+#include "lld/Common/Filesystem.h"
 #include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
 #include "llvm/ADT/STLExtras.h"
@@ -42,18 +43,6 @@ using namespace llvm::object;
 using namespace lld;
 using namespace lld::coff;
 
-// Creates an empty file to and returns a raw_fd_ostream to write to it.
-static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
-  std::error_code ec;
-  auto ret =
-      std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
-  if (ec) {
-    error("cannot open " + file + ": " + ec.message());
-    return nullptr;
-  }
-  return ret;
-}
-
 std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
   return lto::getThinLTOOutputFile(path, ctx.config.thinLTOPrefixReplaceOld,
                                    ctx.config.thinLTOPrefixReplaceNew);
@@ -98,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/Common/Filesystem.cpp b/lld/Common/Filesystem.cpp
index 671b352a3f6bc8f..1fc19ef02788603 100644
--- a/lld/Common/Filesystem.cpp
+++ b/lld/Common/Filesystem.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "lld/Common/Filesystem.h"
+#include "lld/Common/ErrorHandler.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/FileOutputBuffer.h"
 #include "llvm/Support/FileSystem.h"
@@ -57,7 +58,7 @@ void lld::unlinkAsync(StringRef path) {
   //
   // The code here allows LLD to work on all versions of Windows.
   // However, at Windows 10 1903 it seems that the behavior of
-  // Windows has changed, so that we could simply delete the output 
+  // Windows has changed, so that we could simply delete the output
   // file. This code should be simplified once support for older
   // versions of Windows is dropped.
   //
@@ -127,3 +128,28 @@ std::error_code lld::tryCreateFile(StringRef path) {
     return std::error_code();
   return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError());
 }
+
+// Creates an empty file to and returns a raw_fd_ostream to write to it.
+std::unique_ptr<raw_fd_ostream> lld::openFile(StringRef file) {
+  std::error_code ec;
+  auto ret =
+      std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
+  if (ec) {
+    error("cannot open " + file + ": " + ec.message());
+    return nullptr;
+  }
+  return ret;
+}
+
+// The merged bitcode after LTO is large. Try opening a file stream that
+// supports reading, seeking and writing. Such a file allows BitcodeWriter to
+// flush buffered data to reduce memory consumption. If this fails, open a file
+// stream that supports only write.
+std::unique_ptr<raw_fd_ostream> lld::openLTOOutputFile(StringRef file) {
+  std::error_code ec;
+  std::unique_ptr<raw_fd_ostream> fs =
+      std::make_unique<raw_fd_stream>(file, ec);
+  if (!ec)
+    return fs;
+  return openFile(file);
+}
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index eb8f8d7f829a6f0..0f6a9aa8af1d050 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -13,6 +13,7 @@
 #include "Symbols.h"
 #include "lld/Common/Args.h"
 #include "lld/Common/ErrorHandler.h"
+#include "lld/Common/FileSystem.h"
 #include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
 #include "llvm/ADT/SmallString.h"
@@ -40,32 +41,6 @@ using namespace llvm::ELF;
 using namespace lld;
 using namespace lld::elf;
 
-// Creates an empty file to store a list of object files for final
-// linking of distributed ThinLTO.
-static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
-  std::error_code ec;
-  auto ret =
-      std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
-  if (ec) {
-    error("cannot open " + file + ": " + ec.message());
-    return nullptr;
-  }
-  return ret;
-}
-
-// The merged bitcode after LTO is large. Try opening a file stream that
-// supports reading, seeking and writing. Such a file allows BitcodeWriter to
-// flush buffered data to reduce memory consumption. If this fails, open a file
-// stream that supports only write.
-static std::unique_ptr<raw_fd_ostream> openLTOOutputFile(StringRef file) {
-  std::error_code ec;
-  std::unique_ptr<raw_fd_ostream> fs =
-      std::make_unique<raw_fd_stream>(file, ec);
-  if (!ec)
-    return fs;
-  return openFile(file);
-}
-
 static std::string getThinLTOOutputFile(StringRef modulePath) {
   return lto::getThinLTOOutputFile(modulePath, config->thinLTOPrefixReplaceOld,
                                    config->thinLTOPrefixReplaceNew);
diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp
index a5b0f4d3d2a746d..3077bd4a5dcec8f 100644
--- a/lld/MachO/LTO.cpp
+++ b/lld/MachO/LTO.cpp
@@ -15,6 +15,7 @@
 
 #include "lld/Common/Args.h"
 #include "lld/Common/CommonLinkerContext.h"
+#include "lld/Common/FileSystem.h"
 #include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
@@ -32,19 +33,6 @@ using namespace llvm;
 using namespace llvm::MachO;
 using namespace llvm::sys;
 
-// Creates an empty file to store a list of object files for final
-// linking of distributed ThinLTO.
-static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
-  std::error_code ec;
-  auto ret =
-      std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
-  if (ec) {
-    error("cannot open " + file + ": " + ec.message());
-    return nullptr;
-  }
-  return ret;
-}
-
 static std::string getThinLTOOutputFile(StringRef modulePath) {
   return lto::getThinLTOOutputFile(modulePath, config->thinLTOPrefixReplaceOld,
                                    config->thinLTOPrefixReplaceNew);
diff --git a/lld/include/lld/Common/Filesystem.h b/lld/include/lld/Common/Filesystem.h
index 63a0f554a06c57f..61b32eec2ee7d7d 100644
--- a/lld/include/lld/Common/Filesystem.h
+++ b/lld/include/lld/Common/Filesystem.h
@@ -10,11 +10,15 @@
 #define LLD_FILESYSTEM_H
 
 #include "lld/Common/LLVM.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
 #include <system_error>
 
 namespace lld {
 void unlinkAsync(StringRef path);
 std::error_code tryCreateFile(StringRef path);
+std::unique_ptr<llvm::raw_fd_ostream> openFile(StringRef file);
+std::unique_ptr<llvm::raw_fd_ostream> openLTOOutputFile(StringRef file);
 } // namespace lld
 
 #endif
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
+}

@mizvekov mizvekov force-pushed the lld_coff_implement_emitllvm branch from 1cbb665 to 696eb4f Compare September 21, 2023 00:42
@mstorsjo
Copy link
Member

I think the first commit looks good to go at this point; do you want to make a separate PR for that, that we can approve right away?

@aganea
Copy link
Member

aganea commented Sep 27, 2023

I lean on to what Martin says, I am not super excited about re-using /opt: for this purpose here (I do find the PR useful otherwise). Maybe /lldemitasm, /lldemitllvm unless someone has better naming?

@mizvekov
Copy link
Contributor Author

I lean on to what Martin says, I am not super excited about re-using /opt: for this purpose here (I do find the PR useful otherwise). Maybe /lldemitasm, /lldemitllvm unless someone has better naming?

I sure don't mind changing the flag to whatever is the best fit. I can wait for a second opinion from @rnk , and we can debate about the spelling all we want :)

@mizvekov mizvekov force-pushed the lld_coff_implement_emitllvm branch from 5151c02 to b5e750e Compare September 27, 2023 22:10
@mizvekov
Copy link
Contributor Author

I think /lldemitasm is good, in that it's less risky of future collisions with link.exe, but at the same time I find it hard on the eyes.
I wish we grouped all those lld-link exclusive options with a separator similar to /opt, something like /lld:emitllvm is nicer to read.

Another option is to have an /outtype flag that would default to obj, but also accept llvm and asm.
But that is still an lld-link exclusive option, and /lldouttype is also hard on the eyes.

@tru
Copy link
Collaborator

tru commented Sep 29, 2023

maybe /lldemit:asm /lldemit:llvm?

@mizvekov mizvekov force-pushed the lld_coff_implement_emitllvm branch from b5e750e to 4c14e86 Compare September 29, 2023 10:53
@mizvekov mizvekov changed the title [lld][COFF][LTO] Implement /opt:emitllvm option [lld][COFF][LTO] Implement /lldemit:llvm option Sep 29, 2023
@mizvekov
Copy link
Contributor Author

mizvekov commented Oct 2, 2023

I have updated the MR (and subsequent emitasm one) to use a standalone flag.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good overall, one minor nit. I'd love to hear more acks about the option naming from others like @aganea or @rnk here, but if nobody else wants to speak up, I'd be ok to approve this.

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.
@mizvekov mizvekov force-pushed the lld_coff_implement_emitllvm branch from 4c14e86 to 7e5bb36 Compare October 2, 2023 16:22
@aganea
Copy link
Member

aganea commented Oct 2, 2023

I think this looks good overall, one minor nit. I'd love to hear more acks about the option naming from others like @aganea or @rnk here, but if nobody else wants to speak up, I'd be ok to approve this.

Naming SGTM.

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the new name, thanks for the update.

@mizvekov mizvekov merged commit 3923e61 into llvm:main Oct 2, 2023
@mizvekov mizvekov deleted the lld_coff_implement_emitllvm branch October 2, 2023 20:55
@gregrodgers
Copy link
Contributor

My build is failing with this change.
FAILED: lib/liblldCOFF.so.18git
: && /usr/bin/c++ -fPIC -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -fdiagnostics-color -ffunction-sections -fdata-sections -O3 -DNDEBUG -Wl,-z,defs -Wl,-z,nodelete -Wl,-rpath-link,/home/grodgers/git/trunk18.0/build/llvm-project/./lib -Wl,--gc-sections -shared -Wl,-soname,liblldCOFF.so.18git -o lib/liblldCOFF.so.18git tools/lld/COFF/CMakeFiles/lldCOFF.dir/CallGraphSort.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/Chunks.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/COFFLinkerContext.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/DebugTypes.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/DLL.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/Driver.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/DriverUtils.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/ICF.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/InputFiles.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/LLDMapFile.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/LTO.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/MapFile.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/MarkLive.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/MinGW.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/SymbolTable.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/Symbols.cpp.o tools/lld/COFF/CMakeFiles/lldCOFF.dir/Writer.cpp.o -Wl,-rpath,"$ORIGIN/../lib:/home/grodgers/git/trunk18.0/build/llvm-project/lib:" lib/liblldCommon.so.18git -lpthread lib/libLLVMX86CodeGen.so.18git lib/libLLVMX86AsmParser.so.18git lib/libLLVMX86Desc.so.18git lib/libLLVMX86Disassembler.so.18git lib/libLLVMX86Info.so.18git lib/libLLVMAMDGPUCodeGen.so.18git lib/libLLVMAMDGPUAsmParser.so.18git lib/libLLVMAMDGPUDisassembler.so.18git lib/libLLVMLibDriver.so.18git lib/libLLVMLTO.so.18git lib/libLLVMPasses.so.18git lib/libLLVMWindowsDriver.so.18git lib/libLLVMWindowsManifest.so.18git lib/libLLVMAMDGPUDesc.so.18git lib/libLLVMAMDGPUInfo.so.18git lib/libLLVMAMDGPUUtils.so.18git lib/libLLVMDebugInfoDWARF.so.18git lib/libLLVMDebugInfoPDB.so.18git lib/libLLVMDebugInfoMSF.so.18git lib/libLLVMObject.so.18git lib/libLLVMMC.so.18git lib/libLLVMDebugInfoCodeView.so.18git lib/libLLVMCore.so.18git lib/libLLVMBinaryFormat.so.18git lib/libLLVMOption.so.18git lib/libLLVMTargetParser.so.18git lib/libLLVMSupport.so.18git lib/libLLVMDemangle.so.18git -Wl,-rpath-link,/home/grodgers/git/trunk18.0/build/llvm-project/lib && :
/usr/bin/ld: tools/lld/COFF/CMakeFiles/lldCOFF.dir/LTO.cpp.o: in function std::_Function_handler<bool (unsigned int, llvm::Module const&), lld::coff::BitcodeCompiler::createConfig()::{lambda(unsigned long, llvm::Module const&)#1}>::_M_invoke(std::_Any_data const&, unsigned int&&, llvm::Module const&)': LTO.cpp:(.text._ZNSt17_Function_handlerIFbjRKN4llvm6ModuleEEZN3lld4coff15BitcodeCompiler12createConfigEvEUlmS3_E_E9_M_invokeERKSt9_Any_dataOjS3_+0x50): undefined reference to llvm::WriteBitcodeToFile(llvm::Module const&, llvm::raw_ostream&, bool, llvm::ModuleSummaryIndex const*, bool, std::array<unsigned int, 5ul>*)'
collect2: error: ld returned 1 exit status

@mizvekov
Copy link
Contributor Author

mizvekov commented Oct 2, 2023

Can you confirm this patch would help you?

diff --git a/lld/COFF/CMakeLists.txt b/lld/COFF/CMakeLists.txt
index acbd2e5fe2a5..2bbadf75bfa1 100644
--- a/lld/COFF/CMakeLists.txt
+++ b/lld/COFF/CMakeLists.txt
@@ -25,6 +25,7 @@ add_lld_library(lldCOFF
   LINK_COMPONENTS
   ${LLVM_TARGETS_TO_BUILD}
   BinaryFormat
+  BitWriter
   Core
   DebugInfoCodeView
   DebugInfoDWARF

@gregrodgers
Copy link
Contributor

Yes, the above cmake fix corrected my build fail. I look forward to your merge of the above cmake fix. Thank you.

@mizvekov
Copy link
Contributor Author

mizvekov commented Oct 2, 2023

It's done, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants