diff --git a/include/eld/Config/GeneralOptions.h b/include/eld/Config/GeneralOptions.h index 1c2d3e277..27db7e820 100644 --- a/include/eld/Config/GeneralOptions.h +++ b/include/eld/Config/GeneralOptions.h @@ -408,6 +408,9 @@ class GeneralOptions { return UnparsedLTOOptions; } + void setLTOPartitions(unsigned V) { LTOPartitions = V; } + unsigned getLTOPartitions() const { return LTOPartitions; } + void getSymbolsFromFile(llvm::StringRef Filename, std::vector &); void setCopyFarCallsFromFile(std::string File) { @@ -1214,6 +1217,7 @@ class GeneralOptions { std::vector UnparsedLTOOptions; // Unparsed -flto-options, to pass to plugin. uint32_t LTOOptions = 0; // -flto-options + unsigned LTOPartitions = 1; // --lto-partitions= bool Verify = true; // Linker verifies output file. bool Colormap = false; // Map file with color. bool EnableThreads = true; // threads enabled ? diff --git a/include/eld/Driver/GnuLdDriver.h b/include/eld/Driver/GnuLdDriver.h index d6321edf3..a556153b6 100644 --- a/include/eld/Driver/GnuLdDriver.h +++ b/include/eld/Driver/GnuLdDriver.h @@ -145,6 +145,8 @@ class DLL_A_EXPORT GnuLdDriver { getAllArgs(const std::vector &allArgs, const std::vector &ELDFlagsArgs) const; + bool isRunLTOOnly() const { return m_RunLTOOnly; } + protected: int getInteger(llvm::opt::InputArgList &Args, unsigned Key, int Default) const; @@ -191,6 +193,10 @@ class DLL_A_EXPORT GnuLdDriver { DriverFlavor m_DriverFlavor; std::vector m_SupportedTargets; std::string LinkerProgramName; + + // In LTO, only invoke compiler to generate the output file, but do not link + // it. This option is used by --lto-emit-llvm and --lto-emit-asm options. + bool m_RunLTOOnly = false; }; #endif diff --git a/include/eld/Driver/GnuLinkerOptions.td b/include/eld/Driver/GnuLinkerOptions.td index 128bd7038..9bb1c2e92 100644 --- a/include/eld/Driver/GnuLinkerOptions.td +++ b/include/eld/Driver/GnuLinkerOptions.td @@ -1089,6 +1089,16 @@ defm dwodir "Directory to save .dwo files when split DWARF is used in LTO">, Group; +def lto_emit_asm : FF<"lto-emit-asm">, HelpText<"Emit assembly code">; +def plugin_opt_emit_asm : F<"plugin-opt=emit-asm">, + Alias, + HelpText<"Alias for --lto-emit-asm">; + +def lto_emit_llvm : FF<"lto-emit-llvm">, HelpText<"Emit LLVM-IR bitcode">; +def plugin_opt_emit_llvm : F<"plugin-opt=emit-llvm">, + Alias, + HelpText<"Alias for --lto-emit-llvm">; + def lto_O : JJ<"lto-O">, MetaVarName<"">, HelpText<"Optimization level for LTO">; @@ -1104,6 +1114,12 @@ defm plugin_opt_sample_profile "Alias for -lto-sample-profile=">, Group; +def lto_partitions : JJ<"lto-partitions=">, + HelpText<"Number of LTO codegen partitions">; +def : J<"plugin-opt=lto-partitions=">, + Alias, + HelpText<"Alias for --lto-partitions">; + def lto_debug_pass_manager : FF<"lto-debug-pass-manager">, HelpText<"Debug new pass manager">, Group; diff --git a/include/eld/Object/ObjectLinker.h b/include/eld/Object/ObjectLinker.h index e4e362aa5..81f91ed83 100644 --- a/include/eld/Object/ObjectLinker.h +++ b/include/eld/Object/ObjectLinker.h @@ -365,13 +365,14 @@ class ObjectLinker { } private: - std::unique_ptr ltoInit(llvm::lto::Config Conf); + std::unique_ptr ltoInit(llvm::lto::Config Conf, + bool CompileToAssembly); bool finalizeLtoSymbolResolution(llvm::lto::LTO <O, const std::vector &BitCodeFiles); - bool doLto(llvm::lto::LTO <O); + bool doLto(llvm::lto::LTO <O, bool CompileToAssembly); /// writeRelocationResult - helper function of syncRelocationResult, write /// relocation target data to output @@ -415,8 +416,10 @@ class ObjectLinker { std::string getLTOTempPrefix() const; + std::unique_ptr createLTOOutputFile() const; + llvm::Expected> - createLTOTempFile(size_t Task, const std::string &Suffix, + createLTOTempFile(size_t Task, bool Keep, const std::string &Suffix, llvm::SmallString<256> &FileName) const; /// Adds input files to outputTar if --reproduce option is used diff --git a/lib/Core/Linker.cpp b/lib/Core/Linker.cpp index bbaf23828..f23c9b260 100644 --- a/lib/Core/Linker.cpp +++ b/lib/Core/Linker.cpp @@ -410,6 +410,9 @@ bool Linker::normalize() { if (!ObjLinker->createLTOObject()) return false; + if (Driver->isRunLTOOnly()) + return true; + if (ThisModule->getPrinter()->isVerbose()) ThisConfig->raise(Diag::beginning_post_LTO_phase); LinkerProgress->incrementAndDisplayProgress(); diff --git a/lib/LinkerWrapper/GnuLdDriver.cpp b/lib/LinkerWrapper/GnuLdDriver.cpp index 66222bd62..7faaabd43 100644 --- a/lib/LinkerWrapper/GnuLdDriver.cpp +++ b/lib/LinkerWrapper/GnuLdDriver.cpp @@ -1556,6 +1556,26 @@ bool GnuLdDriver::processLTOOptions(llvm::lto::Config &Conf, if (const auto *Arg = Args.getLastArg(OptTable::dwodir)) Conf.DwoDir = Arg->getValue(); + if (Args.hasArg(OptTable::lto_emit_llvm)) + m_RunLTOOnly = true; + + if (Args.hasArg(OptTable::lto_emit_asm)) { + Conf.CGFileType = CodeGenFileType::AssemblyFile; + Conf.Options.MCOptions.AsmVerbose = true; + m_RunLTOOnly = true; + } + + if (const auto *Arg = Args.getLastArg(OptTable::lto_partitions)) { + llvm::StringRef S = Arg->getValue(); + unsigned Value; + if (S.getAsInteger(0, Value) || Value == 0) { + Config.raise(Diag::invalid_value_for_option) + << Arg->getOption().getPrefixedName() << S; + return false; + } + Config.options().setLTOPartitions(Value); + } + if (const auto *Arg = Args.getLastArg(OptTable::lto_sample_profile)) Conf.SampleProfile = Arg->getValue(); @@ -1731,12 +1751,14 @@ bool GnuLdDriver::doLink(llvm::opt::InputArgList &Args, // llvm::errs() << "prepare: linkStatus: " << linkStatus << "\n"; if (!linkStatus || Config.options().getRecordInputFiles()) handleReproduce(Args, actions, false); - if (linkStatus) - linkStatus = linker.link(); - // llvm::errs() << "link: linkStatus: " << linkStatus << "\n"; + if (!isRunLTOOnly()) { + if (linkStatus) + linkStatus = linker.link(); + // llvm::errs() << "link: linkStatus: " << linkStatus << "\n"; + linker.printLayout(); + } if (!linkStatus || Config.options().getRecordInputFiles()) handleReproduce(Args, actions, true); - linker.printLayout(); linkStatus &= ThisModule->getPluginManager().callDestroyHook(); // llvm::errs() << "destroy hook: linkStatus: " << linkStatus << "\n"; linker.unloadPlugins(); diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp index dc1077ed5..d116dfe33 100644 --- a/lib/Object/ObjectLinker.cpp +++ b/lib/Object/ObjectLinker.cpp @@ -64,6 +64,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -399,13 +400,14 @@ std::string ObjectLinker::getLTOTempPrefix() const { } llvm::Expected> -ObjectLinker::createLTOTempFile(size_t Task, const std::string &Suffix, +ObjectLinker::createLTOTempFile(size_t Task, bool Keep, + const std::string &Suffix, SmallString<256> &FileName) const { int FD; std::error_code EC; std::string ErrMsg; - if (MSaveTemps) { + if (Keep) { FileName = (Twine(MLtoTempPrefix) + Twine(Task) + Twine(".") + Twine(Suffix)) .str(); @@ -424,6 +426,19 @@ ObjectLinker::createLTOTempFile(size_t Task, const std::string &Suffix, return std::make_unique(FD, true); } +std::unique_ptr +ObjectLinker::createLTOOutputFile() const { + int FD; + std::string FileName = ThisConfig.options().outputFileName(); + if (std::error_code EC = llvm::sys::fs::openFileForWrite(FileName, FD)) { + ThisConfig.raise(Diag::fatal_no_codegen_compile) + << FileName << ": " << EC.message(); + return {}; + } + + return std::make_unique(FD, true); +} + void ObjectLinker::addInputFileToTar(const std::string &Name, eld::MappingFile::Kind K) { auto *OutputTar = ThisModule->getOutputTarWriter(); @@ -2605,35 +2620,13 @@ static void ltoDiagnosticHandler(const llvm::DiagnosticInfo &DI) { } } -bool ObjectLinker::runAssembler( - std::vector &Files, std::string RelocModel, - const std::vector &AsmFilesFromLto) { - std::vector FileList; - - if (ThisConfig.options().hasLTOAsmFile()) { - for (auto &F : ThisConfig.options().ltoAsmFile()) { - ThisConfig.raise(Diag::using_lto_asm_file) << F; - FileList.push_back(F); - } - } else { - for (auto &F : AsmFilesFromLto) { - if (!F.empty()) - FileList.push_back(F); - } - } - - if (ThisConfig.options().hasLTOOutputFile()) { - for (auto &F : ThisConfig.options().ltoOutputFile()) { - ThisConfig.raise(Diag::using_lto_output_file) << F; - Files.push_back(F); - } - return true; - } - +bool ObjectLinker::runAssembler(std::vector &Files, + std::string RelocModel, + const std::vector &FileList) { uint32_t Count = 0; - for (auto F : FileList) { + for (const auto &F : FileList) { SmallString<256> OutputFileName; - auto OS = createLTOTempFile(Count, "o", OutputFileName); + auto OS = createLTOTempFile(Count, MSaveTemps, "o", OutputFileName); if (!OS) return false; Files.push_back(std::string(OutputFileName)); @@ -2647,7 +2640,8 @@ bool ObjectLinker::runAssembler( return true; } -std::unique_ptr ObjectLinker::ltoInit(llvm::lto::Config Conf) { +std::unique_ptr ObjectLinker::ltoInit(llvm::lto::Config Conf, + bool CompileToAssembly) { // Parse codegen options and pre-initialize the config eld::RegisterTimer T("Initialize LTO", "LTO", ThisConfig.options().printTimingStats()); @@ -2681,7 +2675,7 @@ std::unique_ptr ObjectLinker::ltoInit(llvm::lto::Config Conf) { Conf.DefaultTriple = ThisConfig.targets().triple().str(); - if (getTargetBackend().ltoNeedAssembler()) + if (CompileToAssembly) Conf.CGFileType = llvm::CodeGenFileType::AssemblyFile; auto Model = ltoCodegenModel(); @@ -2716,7 +2710,9 @@ std::unique_ptr ObjectLinker::ltoInit(llvm::lto::Config Conf) { // Initialize the LTO backend llvm::lto::ThinBackend Backend = llvm::lto::createInProcessThinBackend( llvm::heavyweight_hardware_concurrency(NumThreads)); - return std::make_unique(std::move(Conf), std::move(Backend)); + return std::make_unique( + std::move(Conf), std::move(Backend), + ThisConfig.options().getLTOPartitions()); } bool ObjectLinker::finalizeLtoSymbolResolution( @@ -2972,20 +2968,19 @@ void ObjectLinker::addLTOOutputToTar() { } } -bool ObjectLinker::doLto(llvm::lto::LTO <O) { +bool ObjectLinker::doLto(llvm::lto::LTO <O, bool CompileToAssembly) { eld::RegisterTimer T("Invoke LTO", "LTO", ThisConfig.options().printTimingStats()); // Run LTO std::vector Files; - if (!ThisConfig.options().hasLTOOutputFile()) - Files.resize(LTO.getMaxTasks()); - FilesToRemove.resize(LTO.getMaxTasks()); + bool KeepLTOOutput = + MSaveTemps || ThisModule->getLinker()->getLinkerDriver()->isRunLTOOnly(); auto AddStream = [&](size_t Task, const Twine &ModuleName) -> llvm::Expected> { SmallString<256> ObjFileName; - auto OS = createLTOTempFile( - Task, getTargetBackend().ltoNeedAssembler() ? "s" : "o", ObjFileName); + auto OS = createLTOTempFile(Task, KeepLTOOutput, + CompileToAssembly ? "s" : "o", ObjFileName); if (!OS) { ThisModule->setFailure(true); @@ -2994,8 +2989,6 @@ bool ObjectLinker::doLto(llvm::lto::LTO <O) { assert(Files[Task].empty() && "LTO task already produced an output!"); Files[Task] = std::string(ObjFileName); - if (!MSaveTemps) - FilesToRemove[Task] = std::string(ObjFileName); return std::make_unique(std::move(*OS)); }; @@ -3036,45 +3029,48 @@ bool ObjectLinker::doLto(llvm::lto::LTO <O) { ThinLTOCache = *LC; } - if (getTargetBackend().ltoNeedAssembler()) { - // If the output files haven't already been provided, run compilation now - std::vector LTOAsmOutput; - if (!ThisConfig.options().hasLTOOutputFile() && - !ThisConfig.options().hasLTOAsmFile()) { - if (llvm::Error E = LTO.run(AddStream, ThinLTOCache)) { - ThisConfig.raise(Diag::fatal_no_codegen_compile) - << llvm::toString(std::move(E)); - ThisModule->setFailure(true); - return false; - } - // AddStream adds the LTO output files to 'files', but in this case - // they're just the input files to the assembler so we need to move them - // to their own array. ltoRunAssembler will add the assembled objects to - // files. - LTOAsmOutput = std::move(Files); - } - if (!runAssembler(Files, ltoCodegenModel().second, LTOAsmOutput)) { - ThisConfig.raise(Diag::lto_codegen_error) << "Assembler error occurred"; - ThisModule->setFailure(true); - return false; + if (ThisConfig.options().hasLTOOutputFile()) { + for (auto &F : ThisConfig.options().ltoOutputFile()) { + ThisConfig.raise(Diag::using_lto_output_file) << F; + Files.push_back(F); } - - if (!MSaveTemps && !ThisConfig.options().hasLTOOutputFile()) - FilesToRemove.insert(FilesToRemove.end(), Files.begin(), Files.end()); } else { - // If the output files haven't already been provided, run compilation now - if (!ThisConfig.options().hasLTOOutputFile()) { + std::vector LTOAsmInput; + if (!ThisConfig.options().hasLTOAsmFile()) { + // Create a separate file for each task, which they will access by its + // index. + Files.resize(LTO.getMaxTasks()); if (llvm::Error E = LTO.run(AddStream, ThinLTOCache)) { ThisConfig.raise(Diag::fatal_no_codegen_compile) << llvm::toString(std::move(E)); ThisModule->setFailure(true); return false; } + llvm::erase_if(Files, [](const std::string &x) { return x.empty(); }); + if (!KeepLTOOutput) + FilesToRemove.insert(FilesToRemove.end(), Files.begin(), Files.end()); + if (CompileToAssembly) { + // AddStream adds the LTO output files to 'Files', but in this case + // they're just the input files to the assembler so we need to move them + // to their own array. ltoRunAssembler will add the assembled objects to + // files. + LTOAsmInput = std::move(Files); + } } else { - for (auto &F : ThisConfig.options().ltoOutputFile()) { - ThisConfig.raise(Diag::using_lto_output_file) << F; - Files.push_back(F); + for (const auto &F : ThisConfig.options().ltoAsmFile()) { + ThisConfig.raise(Diag::using_lto_asm_file) << F; + LTOAsmInput.push_back(F); + } + } + if (!ThisModule->getLinker()->getLinkerDriver()->isRunLTOOnly() && + !LTOAsmInput.empty()) { + if (!runAssembler(Files, ltoCodegenModel().second, LTOAsmInput)) { + ThisConfig.raise(Diag::lto_codegen_error) << "Assembler error occurred"; + ThisModule->setFailure(true); + return false; } + if (!KeepLTOOutput) + FilesToRemove.insert(FilesToRemove.end(), Files.begin(), Files.end()); } } if (!ThisConfig.getDiagEngine()->diagnose()) { @@ -3085,8 +3081,6 @@ bool ObjectLinker::doLto(llvm::lto::LTO <O) { // Add the generated object files into the InputBuilder structure for (auto &F : Files) { - if (F.empty()) - continue; LtoObjects.push_back(F); if (MSaveTemps) ThisConfig.raise(Diag::note_temp_lto_object) << F; @@ -3178,10 +3172,6 @@ bool ObjectLinker::createLTOObject(void) { std::vector Options; processCodegenOptions(ThisConfig.options().codeGenOpts(), Conf, Options); - if (!ThisModule->getLinker()->getLinkerDriver()->processLTOOptions(Conf, - Options)) - return false; - getTargetBackend().AddLTOOptions(Options); if (LTOPlugin) @@ -3196,6 +3186,12 @@ bool ObjectLinker::createLTOObject(void) { Conf.Options = llvm::codegen::InitTargetOptionsFromCodeGenFlags( ThisConfig.targets().triple()); + // processLTOOptions() was moved after setting Conf.Options from command line + // because it also sets Conf.Options. + if (!ThisModule->getLinker()->getLinkerDriver()->processLTOOptions(Conf, + Options)) + return false; + if (LTOPlugin) { // TODO: Why do we read all relocations here? Get rid of? @@ -3207,14 +3203,26 @@ bool ObjectLinker::createLTOObject(void) { PrepareDiagEngineForLTO PrepareDiagnosticsForLto(ThisConfig.getDiagEngine()); - std::unique_ptr LTO = ltoInit(std::move(Conf)); + // CGFileType may have been set to AssemblyFile by `--lto-emit-asm`. + bool CompileToAssembly = Conf.CGFileType == CodeGenFileType::AssemblyFile || + getTargetBackend().ltoNeedAssembler(); + if (ThisModule->getLinker()->getLinkerDriver()->isRunLTOOnly() && + !CompileToAssembly) { + Conf.PreCodeGenModuleHook = [this](size_t task, const llvm::Module &m) { + if (auto os = createLTOOutputFile()) + WriteBitcodeToFile(m, *os, false); + return false; + }; + } + std::unique_ptr LTO = + ltoInit(std::move(Conf), CompileToAssembly); if (!LTO) return false; if (!finalizeLtoSymbolResolution(*LTO, BitCodeInputs)) return false; - if (!doLto(*LTO)) { + if (!doLto(*LTO, CompileToAssembly)) { ThisModule->setFailure(true); return false; } diff --git a/test/Hexagon/standalone/LTO/LTOReuseFiles/ltoreusefiles.test b/test/Hexagon/standalone/LTO/LTOReuseFiles/ltoreusefiles.test index 75849947b..0009b4f19 100644 --- a/test/Hexagon/standalone/LTO/LTOReuseFiles/ltoreusefiles.test +++ b/test/Hexagon/standalone/LTO/LTOReuseFiles/ltoreusefiles.test @@ -19,8 +19,7 @@ RUN: %readelf -s -W %t2.out.asmoutputoverride | %filecheck %s -check-prefix=ASMO #ASMOVERRIDE: Using LTO assembly file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto2.s #OUTPUTOVERRIDE: Using LTO output file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto1.o #OUTPUTOVERRIDE: Using LTO output file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto2.o -#ASMOUTPUTOVERRIDE: Using LTO assembly file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto1.s -#ASMOUTPUTOVERRIDE: Using LTO assembly file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto2.s +#ASMOUTPUTOVERRIDE-NOT: Using LTO assembly file #ASMOUTPUTOVERRIDE: Using LTO output file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto1.o #ASMOUTPUTOVERRIDE: Using LTO output file : {{[ -\(\)_A-Za-z0-9.\\/:]+}}lto2.o #ASMOVERRIDESYMS: bar diff --git a/test/lit.cfg b/test/lit.cfg index b585bd7be..3170974e9 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -158,6 +158,7 @@ elfcopy = 'llvm-objcopy' strip = 'llvm-strip' strings = 'llvm-strings' llvmas = 'llvm-as' +llvmdis = 'llvm-dis' llvmmc = 'llvm-mc' obj2yaml = 'obj2yaml' yaml2obj = 'yaml2obj' @@ -305,6 +306,9 @@ if config.test_target == 'x86': nm = 'llvm-nm' objdump = 'llvm-objdump' dwarfdump = 'llvm-dwarfdump' + linkopts = '--thread-count 4 --emit-relocs' + if hasattr(config,'eld_option') and config.eld_option != 'default': + linkopts = '--thread-count 4 ' + config.eld_option config.available_features.add('x86') if config.test_target == 'ARM': @@ -431,6 +435,7 @@ driver = which(driver) driverxx = which(driverxx) llvmmc = which(llvmmc) llvmas = which(llvmas) +llvmdis = which(llvmdis) clangas = which(clangas) clang = which(clang) elfcopy = which(elfcopy) @@ -485,6 +490,7 @@ lit_config.note('using driverxx: {}'.format(driverxx)) lit_config.note('using clangxx: {}'.format(which(clangxx))) lit_config.note('using llvmmc: {}'.format(which(llvmmc))) lit_config.note('using llvmas: {}'.format(which(llvmas))) +lit_config.note('using llvmdis: {}'.format(llvmas)) lit_config.note('using clangas: {}'.format(which(clangas))) lit_config.note('using clang: {}'.format(which(clang))) lit_config.note('using elfcopy: {}'.format(which(elfcopy))) @@ -553,6 +559,7 @@ config.substitutions.append( ("%not","".join(cnot)) ) config.substitutions.append( ("%clangxx","".join(clangxx)) ) config.substitutions.append( ("%llvm-mc","".join(llvmmc)) ) config.substitutions.append( ("%llvm-as","".join(llvmas)) ) +config.substitutions.append( ("%llvm-dis","".join(llvmdis)) ) config.substitutions.append( ("%clangas","".join(clangas)) ) config.substitutions.append( ("%clang","".join(clang)) ) config.substitutions.append( ("%elfcopy","".join(elfcopy)) ) diff --git a/test/lld/ELF/emit-asm.ll b/test/lld/ELF/emit-asm.ll new file mode 100644 index 000000000..3a1422199 --- /dev/null +++ b/test/lld/ELF/emit-asm.ll @@ -0,0 +1,36 @@ +; REQUIRES: x86 + +; RUN: %rm %t.* +; RUN: %llvm-as %s -o %t.bc + +; RUN: %link %linkopts %emulation --lto-emit-asm -shared %t.bc -o %t.out +; RUN: FileCheck %s < %t.out.llvm-lto.0.s +; RUN: not ls %t.out.*.o + +; RUN: %link %linkopts %emulation --plugin-opt=emit-asm --plugin-opt=lto-partitions=2 -shared %t.bc -o %t.out +; RUN: cat %t.out.llvm-lto.0.s %t.out.llvm-lto.1.s | FileCheck %s +; RUN: not ls %t.out.*.o + +; RUN: %link %linkopts --lto-emit-asm --save-temps -shared %t.bc -o %t.out +; RUN: FileCheck --input-file %t.out.llvm-lto.0.s %s +; RUN: not ls %t.out.*.o +; RUN: %llvm-dis %t.out.llvm-lto.0.4.opt.bc -o - | FileCheck --check-prefix=OPT %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +;; Note: we also check for the presence of comments; --lto-emit-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/test/lld/ELF/emit-llvm.ll b/test/lld/ELF/emit-llvm.ll new file mode 100644 index 000000000..954a06c5f --- /dev/null +++ b/test/lld/ELF/emit-llvm.ll @@ -0,0 +1,23 @@ +; REQUIRES: x86 + +; RUN: %rm %t.* +; RUN: opt -module-hash -module-summary %s -o %t.o +; RUN: %link %linkopts %emulation --plugin-opt=emit-llvm -o %t.out.o %t.o +; RUN: %llvm-dis < %t.out.o -o - | FileCheck %s +; RUN: not ls %t.out.*.o + +;; Regression test for D112297: bitcode writer used to crash when +;; --plugin-opt=emit-llvmis enabled and the output is /dev/null. +; RUN: %link %linkopts %emulation --plugin-opt=emit-llvm -mllvm -bitcode-flush-threshold=0 -o /dev/null %t.o +; RUN: %link %linkopts %emulation --lto-emit-llvm -mllvm -bitcode-flush-threshold=0 -o /dev/null %t.o + +; CHECK: define hidden void @main() + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@llvm.compiler.used = appending global [1 x ptr] [ptr @main], section "llvm.metadata" + +define hidden void @main() { + ret void +}