[lld] [lld][COFF][LTO] Implement /opt:emitasm option (PR #67079)
Matheus Izvekov via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 29 04:11:23 PDT 2023
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/67079
>From 4c14e86a87d4f1ceb27c35b25a267003d26e8a63 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 /lldemit:llvm 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 | 3 +++
lld/COFF/Driver.cpp | 14 +++++++++++++-
lld/COFF/LTO.cpp | 9 +++++++++
lld/COFF/Options.td | 1 +
lld/test/COFF/lto-emit-llvm.ll | 14 ++++++++++++++
5 files changed, 40 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..e66ab3a32c56721 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -48,6 +48,8 @@ enum class ExportSource {
ModuleDefinition,
};
+enum class EmitKind { Obj, LLVM };
+
// Represents an /export option.
struct Export {
StringRef name; // N in /export:N or /export:E=N
@@ -311,6 +313,7 @@ struct Configuration {
bool pseudoRelocs = false;
bool stdcallFixup = false;
bool writeCheckSum = false;
+ EmitKind emit = EmitKind::Obj;
};
} // namespace lld::coff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 61a04a74aa60278..7649e1db762e731 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1855,6 +1855,17 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (args.hasArg(OPT_lldsavetemps))
config->saveTemps = true;
+ // Handle /lldemit
+ if (auto *arg = args.getLastArg(OPT_lldemit)) {
+ StringRef s = arg->getValue();
+ if (s == "obj")
+ config->emit = EmitKind::Obj;
+ else if (s == "llvm")
+ config->emit = EmitKind::LLVM;
+ else
+ error("/lldemit: unknown option: " + s);
+ }
+
// Handle /kill-at
if (args.hasArg(OPT_kill_at))
config->killAt = true;
@@ -2395,7 +2406,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, don't emit object files for other /lldemit options.
+ if (config->emit != EmitKind::Obj || 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..6e835d055479c82 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.emit == EmitKind::LLVM) {
+ 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/COFF/Options.td b/lld/COFF/Options.td
index ea4ddb2d849534e..a60df4c9bc7b704 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -76,6 +76,7 @@ def lldltocachepolicy : P<"lldltocachepolicy",
"Pruning policy for the ThinLTO cache">;
def lldsavetemps : F<"lldsavetemps">,
HelpText<"Save intermediate LTO compilation results">;
+def lldemit : P<"lldemit", "Specify output type">;
def machine : P<"machine", "Specify target platform">;
def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
diff --git a/lld/test/COFF/lto-emit-llvm.ll b/lld/test/COFF/lto-emit-llvm.ll
new file mode 100644
index 000000000000000..985058de10a4888
--- /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 /lldemit:llvm /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 6a1d1eb95ad81453283a6f90a1041f85e75e68d7 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 /lldemit:asm 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 | 2 +-
lld/COFF/Driver.cpp | 2 ++
lld/COFF/LTO.cpp | 23 +++++++++++++++++------
lld/test/COFF/lto-emit-asm.ll | 28 ++++++++++++++++++++++++++++
lld/test/COFF/pdb-thinlto.ll | 8 ++++----
5 files changed, 52 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 e66ab3a32c56721..562af7e0d23c19f 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -48,7 +48,7 @@ enum class ExportSource {
ModuleDefinition,
};
-enum class EmitKind { Obj, LLVM };
+enum class EmitKind { Obj, LLVM, ASM };
// Represents an /export option.
struct Export {
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 7649e1db762e731..b9f9438a9f1e301 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1862,6 +1862,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
config->emit = EmitKind::Obj;
else if (s == "llvm")
config->emit = EmitKind::LLVM;
+ else if (s == "asm")
+ config->emit = EmitKind::ASM;
else
error("/lldemit: unknown option: " + s);
}
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 6e835d055479c82..28ce7ae226143bc 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -87,13 +87,21 @@ lto::Config BitcodeCompiler::createConfig() {
c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate;
c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch;
- if (ctx.config.emit == EmitKind::LLVM) {
+ switch (ctx.config.emit) {
+ case EmitKind::Obj:
+ break;
+ case EmitKind::LLVM:
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;
};
+ break;
+ case EmitKind::ASM:
+ c.CGFileType = CodeGenFileType::AssemblyFile;
+ c.Options.MCOptions.AsmVerbose = true;
+ break;
}
if (ctx.config.saveTemps)
@@ -213,6 +221,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files);
std::vector<InputFile *> ret;
+ bool emitASM = ctx.config.emit == EmitKind::ASM;
+ const char *Ext = 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 +245,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 || emitASM)
saveBuffer(buf[i].second, ltoObjName);
- ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
+ if (!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..76bfabd06c58a48
--- /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 /lldemit:asm /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 /lldemit:asm /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; /lldemit:asm 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