Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -1169,8 +1169,7 @@ Config->Manifest = Configuration::SideBySide; else parseManifest(Arg->getValue()); - } - + } // Handle /manifestuac if (auto *Arg = Args.getLastArg(OPT_manifestuac)) parseManifestUAC(Arg->getValue()); Index: COFF/LTO.cpp =================================================================== --- COFF/LTO.cpp +++ COFF/LTO.cpp @@ -99,12 +99,12 @@ void BitcodeCompiler::add(BitcodeFile &F) { lto::InputFile &Obj = *F.Obj; unsigned SymNum = 0; - std::vector SymBodies = F.getSymbols(); - std::vector Resols(SymBodies.size()); + std::vector Syms = F.getSymbols(); + std::vector Resols(Syms.size()); // Provide a resolution to the LTO API for each symbol. for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { - Symbol *Sym = SymBodies[SymNum]; + Symbol *Sym = Syms[SymNum]; lto::SymbolResolution &R = Resols[SymNum]; ++SymNum; Index: ELF/LTO.cpp =================================================================== --- ELF/LTO.cpp +++ ELF/LTO.cpp @@ -163,6 +163,7 @@ BitcodeCompiler::~BitcodeCompiler() = default; static void undefine(Symbol *S) { + dbgs() << "undefine\n"; replaceSymbol(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT, S->Type); } @@ -244,6 +245,7 @@ unsigned MaxTasks = LTOObj->getMaxTasks(); Buf.resize(MaxTasks); Files.resize(MaxTasks); + dbgs() << "MaxTasks " << MaxTasks << "\n"; // The --thinlto-cache-dir option specifies the path to a directory in which // to cache native object files for ThinLTO incremental builds. If a path was Index: test/wasm/Inputs/start.ll =================================================================== --- test/wasm/Inputs/start.ll +++ test/wasm/Inputs/start.ll @@ -1,4 +1,5 @@ target triple = "wasm32-unknown-unknown" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" define void @_start() local_unnamed_addr { entry: Index: test/wasm/lto-plugin-ignore.test =================================================================== --- /dev/null +++ test/wasm/lto-plugin-ignore.test @@ -0,0 +1,8 @@ +RUN: llc -filetype=obj %p/Inputs/start.ll -o %t +RUN: wasm-ld %t -plugin-opt=/foo/bar -plugin-opt=-fresolution=zed \ +RUN: -plugin-opt=-pass-through=-lgcc -plugin-opt=-function-sections \ +RUN: -plugin-opt=-data-sections -plugin-opt=thinlto -o /dev/null + +RUN: not wasm-ld %t -plugin-opt=-abc -plugin-opt=-xyz -o /dev/null 2>&1 | FileCheck %s +CHECK: error: --plugin-opt: wasm-ld{{.*}}: Unknown command line argument '-abc' +CHECK: error: --plugin-opt: wasm-ld{{.*}}: Unknown command line argument '-xyz' Index: test/wasm/lto/lto-start.ll =================================================================== --- /dev/null +++ test/wasm/lto/lto-start.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as %s -o %t.o +; RUN: wasm-ld %t.o -o %t.wasm +; RUN: obj2yaml %t.wasm | FileCheck %s + +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: __wasm_call_ctors +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: _start + + +target triple = "wasm32-unknown-unknown-wasm" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" + +define void @_start() { + ret void +} Index: wasm/CMakeLists.txt =================================================================== --- wasm/CMakeLists.txt +++ wasm/CMakeLists.txt @@ -6,6 +6,7 @@ Driver.cpp InputChunks.cpp InputFiles.cpp + LTO.cpp MarkLive.cpp OutputSections.cpp SymbolTable.cpp @@ -18,6 +19,8 @@ BinaryFormat Core Demangle + LTO + MC Object Option Support Index: wasm/Config.h =================================================================== --- wasm/Config.h +++ wasm/Config.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/CachePruning.h" namespace lld { namespace wasm { @@ -28,19 +29,26 @@ bool MergeDataSegments; bool PrintGcSections; bool Relocatable; + bool SaveTemps; bool StripAll; bool StripDebug; bool StackFirst; uint32_t GlobalBase; uint32_t InitialMemory; uint32_t MaxMemory; - uint32_t Optimize; uint32_t ZStackSize; + unsigned LTOPartitions; + unsigned LTOO; + unsigned Optimize; + unsigned ThinLTOJobs; llvm::StringRef Entry; + llvm::StringRef ProgName; llvm::StringRef OutputFile; + llvm::StringRef ThinLTOCacheDir; llvm::StringSet<> AllowUndefinedSymbols; std::vector SearchPaths; + llvm::CachePruningPolicy ThinLTOCachePolicy; }; // The only instance of Configuration struct. Index: wasm/Driver.cpp =================================================================== --- wasm/Driver.cpp +++ wasm/Driver.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/TargetSelect.h" #define DEBUG_TYPE "lld" @@ -48,6 +49,17 @@ #undef OPTION }; +// This function is called on startup. We need this for LTO since +// LTO calls LLVM functions to compile bitcode files to native code. +// Technically this can be delayed until we read bitcode files, but +// we don't bother to do lazily because the initialization is fast. +static void initLLVM() { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); +} + class LinkerDriver { public: void link(ArrayRef ArgsArr); @@ -72,6 +84,7 @@ Config = make(); Symtab = make(); + initLLVM(); LinkerDriver().link(Args); // Exit immediately if we don't need to return to the caller. @@ -169,7 +182,8 @@ return; MemoryBufferRef MBRef = *Buffer; - if (identify_magic(MBRef.getBuffer()) == file_magic::archive) { + switch (identify_magic(MBRef.getBuffer())) { + case file_magic::archive: { SmallString<128> ImportFile = Path; path::replace_extension(ImportFile, ".imports"); if (fs::exists(ImportFile)) @@ -178,8 +192,13 @@ Files.push_back(make(MBRef)); return; } + case file_magic::bitcode: + Files.push_back(make(MBRef)); + break; + default: + Files.push_back(make(MBRef)); + } - Files.push_back(make(MBRef)); } // Add a given library by searching it from input search paths. @@ -257,8 +276,39 @@ } } +static int parseInt(StringRef S, opt::Arg *Arg) { + int V = 0; + if (!to_integer(S, V, 10)) + error(Arg->getSpelling() + "=" + Arg->getValue() + + ": number expected, but got '" + S + "'"); + return V; +} + +static void parseClangOption(StringRef Opt, const Twine &Msg) { + std::string Err; + raw_string_ostream OS(Err); + + const char *Argv[] = {Config->ProgName.data(), Opt.data()}; + if (cl::ParseCommandLineOptions(2, Argv, "", &OS)) + return; + OS.flush(); + error(Msg + ": " + StringRef(Err).trim()); +} + +// Force Sym to be entered in the output. Used for -u or equivalent. +static Symbol *handleUndefined(StringRef Name) { + Symbol *S = Symtab->addUndefinedFunction(Name, 0, nullptr, nullptr); + + // Since symbol S may not be used inside the program, LTO may + // eliminate it. Mark the symbol as "used" to prevent it. + S->IsUsedInRegularObj = true; + + return S; +} + void LinkerDriver::link(ArrayRef ArgsArr) { WasmOptTable Parser; + Config->ProgName = ArgsArr[0]; opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); // Handle --help @@ -273,6 +323,22 @@ return; } + // Parse LTO plugin-related options for compatibility with gold. + for (auto *Arg : Args.filtered(OPT_plugin_opt)) { + StringRef S = Arg->getValue(); + if (S.startswith("O")) + Config->LTOO = parseInt(S.substr(1), Arg); + else if (S.startswith("lto-partitions=")) + Config->LTOPartitions = parseInt(S.substr(15), Arg); + else if (S.startswith("jobs=")) + Config->ThinLTOJobs = parseInt(S.substr(5), Arg); + else if (S.startswith("mcpu=")) + parseClangOption(Saver.save("-" + S), Arg->getSpelling()); + else if (!S.startswith("/") && !S.startswith("-fresolution=") && + !S.startswith("-pass-through=") && !S.startswith("thinlto")) + parseClangOption(S, Arg->getSpelling()); + } + // Parse and evaluate -mllvm options. std::vector V; V.push_back("wasm-ld (LLVM option parsing)"); @@ -290,6 +356,7 @@ Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); Config->ImportMemory = Args.hasArg(OPT_import_memory); Config->ImportTable = Args.hasArg(OPT_import_table); + Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1); Config->Optimize = args::getInteger(Args, OPT_O, 0); Config->OutputFile = Args.getLastArgValue(OPT_o); Config->Relocatable = Args.hasArg(OPT_relocatable); @@ -300,6 +367,7 @@ !Config->Relocatable); Config->PrintGcSections = Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + Config->SaveTemps = Args.hasArg(OPT_save_temps); Config->SearchPaths = args::getStrings(Args, OPT_L); Config->StripAll = Args.hasArg(OPT_strip_all); Config->StripDebug = Args.hasArg(OPT_strip_debug); @@ -368,12 +436,11 @@ // For now, since we don't actually use the start function as the // wasm start symbol, we don't need to care about it signature. if (!Config->Entry.empty()) - EntrySym = - Symtab->addUndefinedFunction(Config->Entry, 0, nullptr, nullptr); + EntrySym = handleUndefined(Config->Entry); // Handle the `--undefined ` options. for (auto *Arg : Args.filtered(OPT_undefined)) - Symtab->addUndefinedFunction(Arg->getValue(), 0, nullptr, nullptr); + handleUndefined(Arg->getValue()); } createFiles(Args); @@ -389,6 +456,12 @@ if (!Config->Relocatable) handleWeakUndefines(); + // Do link-time optimization if given files are LLVM bitcode files. + // This compiles bitcode files into real object files. + Symtab->addCombinedLTOObject(); + if (errorCount()) + return; + // Make sure we have resolved all symbols. if (!Config->Relocatable && !Config->AllowUndefined) { Symtab->reportRemainingUndefines(); Index: wasm/InputFiles.h =================================================================== --- wasm/InputFiles.h +++ wasm/InputFiles.h @@ -14,6 +14,7 @@ #include "lld/Common/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/MemoryBuffer.h" @@ -28,6 +29,12 @@ using llvm::wasm::WasmRelocation; using llvm::wasm::WasmSignature; +namespace llvm { +namespace lto { +class InputFile; +} +} + namespace lld { namespace wasm { @@ -42,6 +49,7 @@ enum Kind { ObjectKind, ArchiveKind, + BitcodeKind, }; virtual ~InputFile() {} @@ -57,9 +65,13 @@ // An archive file name if this file is created from an archive. StringRef ParentName; + ArrayRef getSymbols() const { return Symbols; } + protected: InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} MemoryBufferRef MB; + // List of all symbols referenced or defined by this file. + std::vector Symbols; private: const Kind FileKind; @@ -113,7 +125,6 @@ std::vector CustomSections; llvm::DenseMap CustomSectionsByIndex; - ArrayRef getSymbols() const { return Symbols; } Symbol *getSymbol(uint32_t Index) const { return Symbols[Index]; } FunctionSymbol *getFunctionSymbol(uint32_t Index) const; DataSymbol *getDataSymbol(uint32_t Index) const; @@ -126,12 +137,18 @@ bool isExcludedByComdat(InputChunk *Chunk) const; - // List of all symbols referenced or defined by this file. - std::vector Symbols; - std::unique_ptr WasmObj; }; +class BitcodeFile : public InputFile { +public: + explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {} + static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } + + void parse() override; + std::unique_ptr Obj; +}; + // Opens a given file. llvm::Optional readFile(StringRef Path); Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -369,6 +369,57 @@ Symtab->addFile(Obj); } +static Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &ObjSym, + BitcodeFile &F) { + StringRef NameRef = Saver.save(ObjSym.getName()); + /* + uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL; + uint8_t Visibility = mapVisibility(ObjSym.getVisibility()); + + uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE; + bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable(); + + int C = ObjSym.getComdatIndex(); + if (C != -1 && !KeptComdats[C]) + return Symtab->addUndefined(NameRef, Binding, Visibility, Type, + CanOmitFromDynSym, &F); + + if (ObjSym.isUndefined()) + return Symtab->addUndefined(NameRef, Binding, Visibility, Type, + CanOmitFromDynSym, &F); + + if (ObjSym.isCommon()) + return Symtab->addCommon(NameRef, ObjSym.getCommonSize(), + ObjSym.getCommonAlignment(), Binding, Visibility, + STT_OBJECT, F); + */ + + if (ObjSym.isUndefined()) { + if (ObjSym.isExecutable()) + return Symtab->addUndefinedFunction(NameRef, 0, &F, nullptr); + else + return Symtab->addUndefinedData(NameRef, 0, &F); + } + + if (ObjSym.isExecutable()) + return Symtab->addDefinedFunction(NameRef, 0, &F, nullptr); + else + return Symtab->addDefinedData(NameRef, 0, &F, nullptr, 0, 0); +} + +void BitcodeFile::parse() { + Obj = check(lto::InputFile::create(MemoryBufferRef( + MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier())))); + Triple T(Obj->getTargetTriple()); + if (T.getArch() != Triple::wasm32) { + fatal(toString(MB.getBufferIdentifier()) + ": machine type must be wasm32"); + return; + } + + for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) + Symbols.push_back(createBitcodeSymbol(ObjSym, *this)); +} + // Returns a string in the format of "foo.o" or "foo.a(bar.o)". std::string lld::toString(const wasm::InputFile *File) { if (!File) Index: wasm/LTO.h =================================================================== --- /dev/null +++ wasm/LTO.h @@ -0,0 +1,58 @@ +//===- LTO.h ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a way to combine bitcode files into one wasm +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular wasm files +// but instead LLVM bitcode files. In that case, the linker has to +// convert bitcode files into the native format so that we can create +// a wasm file that contains native code. This file provides that +// functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_WASM_LTO_H +#define LLD_WASM_LTO_H + +#include "lld/Common/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include +#include + +namespace llvm { +namespace lto { +class LTO; +} +} + +namespace lld { +namespace wasm { + +class BitcodeFile; +class InputFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + ~BitcodeCompiler(); + + void add(BitcodeFile &F); + std::vector compile(); + +private: + std::unique_ptr LTOObj; + std::vector> Buff; + std::vector> Files; +}; +} // namespace wasm +} // namespace lld + + +#endif Index: wasm/LTO.cpp =================================================================== --- wasm/LTO.cpp +++ wasm/LTO.cpp @@ -38,14 +38,15 @@ using namespace llvm::object; using namespace lld; -using namespace lld::coff; +using namespace lld::wasm; -static void diagnosticHandler(const DiagnosticInfo &DI) { - SmallString<128> ErrStorage; - raw_svector_ostream OS(ErrStorage); - DiagnosticPrinterRawOStream DP(OS); - DI.print(DP); - warn(ErrStorage); +// This is for use when debugging LTO. +static void saveBuffer(StringRef Buffer, const Twine &Path) { + std::error_code EC; + raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); + if (EC) + error("cannot create " + Path + ": " + EC.message()); + OS << Buffer; } static void checkError(Error E) { @@ -53,40 +54,34 @@ [&](ErrorInfoBase &EIB) { error(EIB.message()); }); } -static void saveBuffer(StringRef Buffer, const Twine &Path) { - std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); - if (EC) - error("cannot create " + Path + ": " + EC.message()); - OS << Buffer; +static void diagnosticHandler(const DiagnosticInfo &DI) { + SmallString<128> S; + raw_svector_ostream OS(S); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + warn(S); } static std::unique_ptr createLTO() { - lto::Config Conf; - Conf.Options = InitTargetOptionsFromCodeGenFlags(); - - // Always emit a section per function/datum with LTO. LLVM LTO should get most - // of the benefit of linker GC, but there are still opportunities for ICF. - Conf.Options.FunctionSections = true; - Conf.Options.DataSections = true; - - // Use static reloc model on 32-bit x86 because it usually results in more - // compact code, and because there are also known code generation bugs when - // using the PIC model (see PR34306). - if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386) - Conf.RelocModel = Reloc::Static; - else - Conf.RelocModel = Reloc::PIC_; - Conf.DisableVerify = true; - Conf.DiagHandler = diagnosticHandler; - Conf.OptLevel = Config->LTOOptLevel; + lto::Config C; + C.Options = InitTargetOptionsFromCodeGenFlags(); + + // Always emit a section per function/datum with LTO. + C.Options.FunctionSections = true; + C.Options.DataSections = true; + + C.DisableVerify = true; + C.DiagHandler = diagnosticHandler; + C.OptLevel = Config->LTOO; + if (Config->SaveTemps) - checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", - /*UseInputModulePath*/ true)); + checkError(C.addSaveTemps(Config->OutputFile.str() + ".", + /*UseInputModulePath*/ true)); + lto::ThinBackend Backend; - if (Config->LTOJobs != 0) - Backend = lto::createInProcessThinBackend(Config->LTOJobs); - return llvm::make_unique(std::move(Conf), Backend, + if (Config->ThinLTOJobs != -1U) + Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); + return llvm::make_unique(std::move(C), Backend, Config->LTOPartitions); } @@ -94,17 +89,24 @@ BitcodeCompiler::~BitcodeCompiler() = default; -static void undefine(Symbol *S) { replaceSymbol(S, S->getName()); } +static void undefine(Symbol *S) { + if (isa(S)) + replaceSymbol(S, S->getName(), 0); + else if (isa(S)) + replaceSymbol(S, S->getName(), 0); + else + llvm_unreachable("unexpected symbol kind"); +} void BitcodeCompiler::add(BitcodeFile &F) { lto::InputFile &Obj = *F.Obj; unsigned SymNum = 0; - std::vector SymBodies = F.getSymbols(); - std::vector Resols(SymBodies.size()); + ArrayRef Syms = F.getSymbols(); + std::vector Resols(Syms.size()); // Provide a resolution to the LTO API for each symbol. for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { - Symbol *Sym = SymBodies[SymNum]; + Symbol *Sym = Syms[SymNum]; lto::SymbolResolution &R = Resols[SymNum]; ++SymNum; @@ -114,7 +116,7 @@ // Once IRObjectFile is fixed to report only one symbol this hack can // be removed. R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F; - R.VisibleToRegularObj = Sym->IsUsedInRegularObj; + R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj; if (R.Prevailing) undefine(Sym); } @@ -128,15 +130,16 @@ Buff.resize(MaxTasks); Files.resize(MaxTasks); - // The /lldltocache option specifies the path to a directory in which to cache - // native object files for ThinLTO incremental builds. If a path was + // The --thinlto-cache-dir option specifies the path to a directory in which + // to cache native object files for ThinLTO incremental builds. If a path was // specified, configure LTO to use it as the cache directory. lto::NativeObjectCache Cache; - if (!Config->LTOCache.empty()) - Cache = check(lto::localCache( - Config->LTOCache, [&](size_t Task, std::unique_ptr MB) { - Files[Task] = std::move(MB); - })); + if (!Config->ThinLTOCacheDir.empty()) + Cache = check( + lto::localCache(Config->ThinLTOCacheDir, + [&](size_t Task, std::unique_ptr MB) { + Files[Task] = std::move(MB); + })); checkError(LTOObj->run( [&](size_t Task) { @@ -145,8 +148,8 @@ }, Cache)); - if (!Config->LTOCache.empty()) - pruneCache(Config->LTOCache, Config->LTOCachePolicy); + if (!Config->ThinLTOCacheDir.empty()) + pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy); std::vector Ret; for (unsigned I = 0; I != MaxTasks; ++I) { @@ -154,16 +157,12 @@ continue; if (Config->SaveTemps) { if (I == 0) - saveBuffer(Buff[I], Config->OutputFile + ".lto.obj"); + saveBuffer(Buff[I], Config->OutputFile + ".lto.o"); else - saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj"); + saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o"); } Ret.emplace_back(Buff[I].data(), Buff[I].size()); } - for (std::unique_ptr &File : Files) - if (File) - Ret.push_back(File->getBuffer()); - return Ret; } Index: wasm/Options.td =================================================================== --- wasm/Options.td +++ wasm/Options.td @@ -136,3 +136,24 @@ def alias_max_memory_m: Flag<["-"], "m">, Alias; def alias_relocatable_r: Flag<["-"], "r">, Alias; def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias; + +// LTO-related options. +def lto_aa_pipeline: J<"lto-aa-pipeline=">, + HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">; +def lto_newpm_passes: J<"lto-newpm-passes=">, + HelpText<"Passes to run during LTO">; +def lto_partitions: J<"lto-partitions=">, + HelpText<"Number of LTO codegen partitions">; +def disable_verify: F<"disable-verify">; +def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, + HelpText<"YAML output file for optimization remarks">; +def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">, + HelpText<"Include hotness information in the optimization remarks file">; +defm plugin_opt: Eq<"plugin-opt">, + HelpText<"specifies LTO options for compatibility with GNU linkers">; +def save_temps: F<"save-temps">; +def thinlto_cache_dir: J<"thinlto-cache-dir=">, + HelpText<"Path to ThinLTO cached object file directory">; +defm thinlto_cache_policy: Eq<"thinlto-cache-policy">, + HelpText<"Pruning policy for the ThinLTO cache">; +def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; Index: wasm/SymbolTable.h =================================================================== --- wasm/SymbolTable.h +++ wasm/SymbolTable.h @@ -11,6 +11,7 @@ #define LLD_WASM_SYMBOL_TABLE_H #include "InputFiles.h" +#include "LTO.h" #include "Symbols.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" @@ -39,8 +40,10 @@ class SymbolTable { public: void addFile(InputFile *File); + void addCombinedLTOObject(); std::vector ObjectFiles; + std::vector BitcodeFiles; std::vector SyntheticFunctions; std::vector SyntheticGlobals; @@ -49,6 +52,7 @@ ArrayRef getSymbols() const { return SymVector; } Symbol *find(StringRef Name); + Symbol *addBitcode(StringRef Name, InputFile *File); Symbol *addDefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, InputFunction *Function); Symbol *addDefinedData(StringRef Name, uint32_t Flags, InputFile *File, @@ -80,6 +84,9 @@ std::vector SymVector; llvm::DenseSet Comdats; + + // For LTO. + std::unique_ptr LTO; }; extern SymbolTable *Symtab; Index: wasm/SymbolTable.cpp =================================================================== --- wasm/SymbolTable.cpp +++ wasm/SymbolTable.cpp @@ -29,10 +29,39 @@ log("Processing: " + toString(File)); File->parse(); + // LLVM bitcode file + if (auto *F = dyn_cast(File)) { + BitcodeFiles.push_back(F); + return; + } + if (auto *F = dyn_cast(File)) ObjectFiles.push_back(F); } +// This function is where all the optimizations of link-time +// optimization happens. When LTO is in use, some input files are +// not in native object file format but in the LLVM bitcode format. +// This function compiles bitcode files into a few big native files +// using LLVM functions and replaces bitcode symbols with the results. +// Because all bitcode files that the program consists of are passed +// to the compiler at once, it can do whole-program optimization. +void SymbolTable::addCombinedLTOObject() { + if (BitcodeFiles.empty()) + return; + + // Compile bitcode files and replace bitcode symbols. + LTO.reset(new BitcodeCompiler); + for (BitcodeFile *F : BitcodeFiles) + LTO->add(*F); + + for (StringRef Filename : LTO->compile()) { + auto *Obj = make(MemoryBufferRef(Filename, "lto.tmp")); + Obj->parse(); + ObjectFiles.push_back(Obj); + } +} + void SymbolTable::reportRemainingUndefines() { SetVector Undefs; for (Symbol *Sym : SymVector) { @@ -64,6 +93,7 @@ if (Sym) return {Sym, false}; Sym = reinterpret_cast(make()); + Sym->IsUsedInRegularObj = false; SymVector.emplace_back(Sym); return {Sym, true}; } @@ -170,6 +200,11 @@ return true; } +Symbol *SymbolTable::addBitcode(StringRef Name, InputFile *File) { + DEBUG(dbgs() << "addBitcode: " << Name << "\n"); + return nullptr; +} + Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, InputFunction *Function) { @@ -178,12 +213,16 @@ bool WasInserted; std::tie(S, WasInserted) = insert(Name); + if (!File || File->kind() == InputFile::ObjectKind) + S->IsUsedInRegularObj = true; + if (WasInserted || S->isLazy()) { replaceSymbol(S, Name, Flags, File, Function); return S; } - checkFunctionType(S, File, &Function->Signature); + if (Function) + checkFunctionType(S, File, &Function->Signature); if (shouldReplace(S, File, Flags)) replaceSymbol(S, Name, Flags, File, Function); @@ -199,6 +238,9 @@ bool WasInserted; std::tie(S, WasInserted) = insert(Name); + if (!File || File->kind() == InputFile::ObjectKind) + S->IsUsedInRegularObj = true; + if (WasInserted || S->isLazy()) { replaceSymbol(S, Name, Flags, File, Segment, Address, Size); return S; @@ -218,6 +260,9 @@ bool WasInserted; std::tie(S, WasInserted) = insert(Name); + if (!File || File->kind() == InputFile::ObjectKind) + S->IsUsedInRegularObj = true; + if (WasInserted || S->isLazy()) { replaceSymbol(S, Name, Flags, File, Global); return S; Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -91,10 +91,13 @@ WasmSymbolType getWasmType() const; + // True if this symbol was referenced by a regular (non-bitcode) object. + unsigned IsUsedInRegularObj : 1; + protected: Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F) - : Name(Name), SymbolKind(K), Flags(Flags), File(F), - Referenced(!Config->GcSections) {} + : IsUsedInRegularObj(false), Name(Name), SymbolKind(K), Flags(Flags), + File(F), Referenced(!Config->GcSections) {} StringRef Name; Kind SymbolKind; @@ -332,7 +335,12 @@ "SymbolUnion not aligned enough"); assert(static_cast(static_cast(nullptr)) == nullptr && "Not a Symbol"); - return new (S) T(std::forward(Arg)...); + + Symbol SymCopy = *S; + + T* S2 = new (S) T(std::forward(Arg)...); + S2->IsUsedInRegularObj = SymCopy.IsUsedInRegularObj; + return S2; } } // namespace wasm Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -99,8 +99,10 @@ } uint32_t FunctionSymbol::getFunctionIndex() const { - if (auto *F = dyn_cast(this)) + if (auto *F = dyn_cast(this)) { + assert(F->Function); return F->Function->getFunctionIndex(); + } assert(FunctionIndex != INVALID_INDEX); return FunctionIndex; }