diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -146,6 +146,7 @@ bool dependentLibraries; bool disableVerify; bool ehFrameHdr; + bool emitAsm; bool emitLLVM; bool emitRelocs; bool enableNewDtags; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -887,6 +887,7 @@ config->ehFrameHdr = args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false); config->emitLLVM = args.hasArg(OPT_plugin_opt_emit_llvm, false); + config->emitAsm = args.hasArg(OPT_lto_emit_asm, false); config->emitRelocs = args.hasArg(OPT_emit_relocs); config->callGraphProfileSort = args.hasFlag( OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true); @@ -1929,6 +1930,12 @@ if (config->emitLLVM) return; + // Likewise, --plugin-opt=emit-asm is an option to make LTO create + // an output file in assembly code and exit, so that you can just get a + // combined asm file. + if (config->emitAsm) + return; + // Apply symbol renames for -wrap. if (!wrapped.empty()) wrapSymbols(wrapped); diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -127,6 +127,9 @@ }; } + if (config->emitAsm) + c.CGFileType = CGFT_AssemblyFile; + if (config->saveTemps) checkError(c.addSaveTemps(config->outputFile.str() + ".", /*UseInputModulePath*/ true)); @@ -303,6 +306,13 @@ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o"); } + if (config->emitAsm) { + saveBuffer(buf[0], config->outputFile); + for (unsigned i = 1; i != maxTasks; ++i) + saveBuffer(buf[i], config->outputFile + Twine(i)); + return {}; + } + std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) if (!buf[i].empty()) diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -473,6 +473,8 @@ HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">; def lto_debug_pass_manager: F<"lto-debug-pass-manager">, HelpText<"Debug new pass manager">; +def lto_emit_asm: J<"lto-emit-asm">, + HelpText<"Emit assembly code">; def lto_new_pass_manager: F<"lto-new-pass-manager">, HelpText<"Use new pass manager">; def lto_newpm_passes: J<"lto-newpm-passes=">, @@ -522,6 +524,8 @@ def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">, HelpText<"Directory to store .dwo files when LTO and debug fission are used">; def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">; +def plugin_opt_emit_asm: F<"plugin-opt=emit-asm">, + Alias, HelpText<"Alias for -lto-emit-asm">; def: J<"plugin-opt=jobs=">, Alias, HelpText<"Alias for -thinlto-jobs">; def: J<"plugin-opt=lto-partitions=">, Alias, HelpText<"Alias for -lto-partitions">; def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">; diff --git a/lld/test/ELF/lto/emit-asm.ll b/lld/test/ELF/lto/emit-asm.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/emit-asm.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as %s -o %t.o +; RUN: ld.lld --lto-emit-asm -shared %t.o -o - | FileCheck %s +; RUN: ld.lld --plugin-opt=emit-asm --plugin-opt=lto-partitions=2\ +; RUN: -shared %t.o -o %t2.s +; RUN: cat %t2.s %t2.s1 > %t3.s +; RUN: FileCheck --input-file %t3.s %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK-DAG: f1: +define void @f1() { + ret void +} + +; CHECK-DAG: f2: +define void @f2() { + ret void +}