Skip to content

Commit

Permalink
[lld][WebAssemlby] Implement --thinlto-object-suffix-replace/--thinlt…
Browse files Browse the repository at this point in the history
…o-prefix-replace (llvm#114625)

Fixes: llvm#79604
  • Loading branch information
sbc100 authored Nov 9, 2024
1 parent 7ec682b commit b70eb86
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 3 deletions.
108 changes: 108 additions & 0 deletions lld/test/wasm/lto/thinlto-emit-index.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
;; Copied from ELF/lto/thinlto-index-only.ll
;; First ensure that the ThinLTO handling in lld handles
;; bitcode without summary sections gracefully and generates index file.
; RUN: rm -rf %t && mkdir %t && cd %t
; RUN: mkdir d
; RUN: llvm-as %s -o 1.o
; RUN: llvm-as %p/Inputs/thinlto.ll -o d/2.o
; RUN: wasm-ld --thinlto-emit-index-files -shared 1.o d/2.o -o 3
; RUN: ls d/2.o.thinlto.bc
; RUN: ls 3
; RUN: wasm-ld -shared 1.o d/2.o -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM

;; Basic ThinLTO tests.
; RUN: opt -module-summary %s -o 1.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o d/2.o
; RUN: opt -module-summary %p/Inputs/thinlto_empty.ll -o 3.o
; RUN: cp 3.o 4.o

;; Ensure lld generates an index and also a binary if requested.
; RUN: wasm-ld --thinlto-emit-index-files -shared 1.o --start-lib d/2.o 3.o --end-lib 4.o -o 4
; RUN: ls 4
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: llvm-bcanalyzer -dump d/2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: llvm-dis < 3.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND3
; RUN: llvm-dis < 4.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND4

; IMPORTS1: d/2.o

;; Ensure lld generates an index and not a binary if both emit-index and index-only are present.
; RUN: wasm-ld --thinlto-emit-index-files --thinlto-index-only -shared 1.o d/2.o -o 5
; RUN: not ls 5

;; Test that LLD generates an empty index even for lazy object file that is not added to link.
;; Test that LLD also generates empty imports file with the --thinlto-emit-imports-files option.
; RUN: rm -f 1.o.thinlto.bc 1.o.imports
; RUN: wasm-ld --thinlto-emit-index-files -shared d/2.o --start-lib 1.o --end-lib \
; RUN: --thinlto-emit-imports-files -o 7
; RUN: ls 7
; RUN: ls 1.o.thinlto.bc
; RUN: ls 1.o.imports

;; Ensure LLD generates an empty index for each bitcode file even if all bitcode files are lazy.
; RUN: rm -f 1.o.thinlto.bc
; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-linux /dev/null -o dummy.o
; RUN: wasm-ld --thinlto-emit-index-files -shared dummy.o --start-lib 1.o --end-lib -o 8
; RUN: ls 8
; RUN: ls 1.o.thinlto.bc

;; Test that LLD errors out when run with suffix replacement, or prefix replacement
; RUN: not wasm-ld --thinlto-emit-index-files -shared d/2.o --start-lib 1.o --end-lib \
; RUN: --thinlto-prefix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR1
; ERR1: --thinlto-prefix-replace is not supported with --thinlto-emit-index-files

; RUN: not wasm-ld --thinlto-emit-index-files -shared d/2.o --start-lib 1.o --end-lib \
; RUN: --thinlto-object-suffix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR2
; ERR2: --thinlto-object-suffix-replace is not supported with --thinlto-emit-index-files

;; But not when passed with index only as well
; RUN: wasm-ld --thinlto-emit-index-files -shared d/2.o --start-lib 1.o --end-lib \
; RUN: --thinlto-prefix-replace="abc;xyz" --thinlto-index-only

; RUN: wasm-ld --thinlto-emit-index-files -shared d/2.o --start-lib 1.o --end-lib \
; RUN: --thinlto-object-suffix-replace="abc;xyz" --thinlto-index-only

; NM: T f

;; The backend index for this module contains summaries from itself and
;; Inputs/thinlto.ll, as it imports from the latter.
; BACKEND1: <MODULE_STRTAB_BLOCK
; BACKEND1-NEXT: <ENTRY {{.*}} record string = '1.o'
; BACKEND1-NEXT: <ENTRY {{.*}} record string = 'd/2.o'
; BACKEND1-NEXT: </MODULE_STRTAB_BLOCK
; BACKEND1: <GLOBALVAL_SUMMARY_BLOCK
; BACKEND1: <VERSION
; BACKEND1: <FLAGS
; BACKEND1: <VALUE_GUID {{.*}} op0={{1|2}} {{op1=3060885059 op2=1207956914|op1=3432075125 op2=3712786831}}
; BACKEND1: <VALUE_GUID {{.*}} op0={{1|2}} {{op1=3060885059 op2=1207956914|op1=3432075125 op2=3712786831}}
; BACKEND1: <COMBINED
; BACKEND1: <COMBINED
; BACKEND1: </GLOBALVAL_SUMMARY_BLOCK

;; The backend index for Input/thinlto.ll contains summaries from itself only,
;; as it does not import anything.
; BACKEND2: <MODULE_STRTAB_BLOCK
; BACKEND2-NEXT: <ENTRY {{.*}} record string = 'd/2.o'
; BACKEND2-NEXT: </MODULE_STRTAB_BLOCK
; BACKEND2-NEXT: <GLOBALVAL_SUMMARY_BLOCK
; BACKEND2-NEXT: <VERSION
; BACKEND2-NEXT: <FLAGS
; BACKEND2-NEXT: <VALUE_GUID {{.*}} op0=1 op1=3060885059 op2=1207956914
; BACKEND2-NEXT: <COMBINED
; BACKEND2-NEXT: </GLOBALVAL_SUMMARY_BLOCK

; BACKEND3: ^0 = flags:

; BACKEND4: ^0 = module: (path: "4.o", hash: (0, 0, 0, 0, 0))

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

declare void @g(...)

define void @f() {
entry:
call void (...) @g()
ret void
}
47 changes: 47 additions & 0 deletions lld/test/wasm/lto/thinlto-object-suffix-replace.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
;; Copied from ELF/lto/thinlto-object-suffix-replace.ll
;; Test to make sure the thinlto-object-suffix-replace option is handled
;; correctly.
; RUN: rm -rf %t && mkdir %t && cd %t

;; Generate bitcode file with summary, as well as a minimized bitcode without
; the debug metadata for the thin link.
; RUN: opt --thinlto-bc %s -thin-link-bitcode-file=1.thinlink.bc -o 1.o

;; First perform the thin link on the normal bitcode file, and save the
;; resulting index.
; RUN: wasm-ld --thinlto-index-only -shared 1.o -o 3
; RUN: cp 1.o.thinlto.bc 1.o.thinlto.bc.orig

;; Next perform the thin link on the minimized bitcode file, and compare dump
;; of the resulting index to the above dump to ensure they are identical.
; RUN: rm -f 1.o.thinlto.bc
;; Make sure it isn't inadvertently using the regular bitcode file.
; RUN: rm -f 1.o
; RUN: wasm-ld --thinlto-index-only --thinlto-object-suffix-replace=".thinlink.bc;.o" \
; RUN: -shared 1.thinlink.bc -o 3
; RUN: cmp 1.o.thinlto.bc.orig 1.o.thinlto.bc

;; Ensure lld generates error if object suffix replace option does not have 'old;new' format
; RUN: rm -f 1.o.thinlto.bc
; RUN: not wasm-ld --thinlto-index-only --thinlto-object-suffix-replace="abc:def" -shared 1.thinlink.bc \
; RUN: -o 3 2>&1 | FileCheck %s --check-prefix=ERR1
; ERR1: --thinlto-object-suffix-replace= expects 'old;new' format, but got abc:def

;; If filename does not end with old suffix, no suffix change should occur,
;; so ".thinlto.bc" will simply be appended to the input file name.
; RUN: rm -f 1.thinlink.bc.thinlto.bc
; RUN: wasm-ld --thinlto-index-only --thinlto-object-suffix-replace=".abc;.o" -shared 1.thinlink.bc -o /dev/null
; RUN: ls 1.thinlink.bc.thinlto.bc

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

define void @f() {
entry:
ret void
}

!llvm.dbg.cu = !{}

!1 = !{i32 2, !"Debug Info Version", i32 3}
!llvm.module.flags = !{!1}
23 changes: 23 additions & 0 deletions lld/test/wasm/lto/thinlto-prefix-replace.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; Copied from ELF/lto/thinlto-prefix-replace.ll
; Check that changing the output path via thinlto-prefix-replace works
; RUN: mkdir -p %t/oldpath
; RUN: opt -module-summary %s -o %t/oldpath/thinlto_prefix_replace.o

; Ensure that there is no existing file at the new path, so we properly
; test the creation of the new file there.
; RUN: rm -f %t/newpath/thinlto_prefix_replace.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only --thinlto-prefix-replace="%t/oldpath/;%t/newpath/" -shared %t/oldpath/thinlto_prefix_replace.o -o %t/thinlto_prefix_replace
; RUN: ls %t/newpath/thinlto_prefix_replace.o.thinlto.bc

; Ensure that lld generates error if prefix replace option does not have 'old;new' format.
; RUN: rm -f %t/newpath/thinlto_prefix_replace.o.thinlto.bc
; RUN: not wasm-ld --thinlto-index-only --thinlto-prefix-replace=abc:def -shared %t/oldpath/thinlto_prefix_replace.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
; ERR: --thinlto-prefix-replace= expects 'old;new' format, but got abc:def

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

define void @f() {
entry:
ret void
}
4 changes: 4 additions & 0 deletions lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ struct Configuration {
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOJobs;
llvm::StringRef thinLTOIndexOnlyArg;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
llvm::StringRef thinLTOPrefixReplaceOld;
llvm::StringRef thinLTOPrefixReplaceNew;
llvm::StringRef thinLTOPrefixReplaceNativeObject;
llvm::StringRef whyExtract;

llvm::StringSet<> allowUndefinedSymbols;
Expand Down
47 changes: 46 additions & 1 deletion lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,33 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
error("no input files");
}

static StringRef getAliasSpelling(opt::Arg *arg) {
if (const opt::Arg *alias = arg->getAlias())
return alias->getSpelling();
return arg->getSpelling();
}

static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
unsigned id) {
auto *arg = args.getLastArg(id);
if (!arg)
return {"", ""};

StringRef s = arg->getValue();
std::pair<StringRef, StringRef> ret = s.split(';');
if (ret.second.empty())
error(getAliasSpelling(arg) + " expects 'old;new' format, but got " + s);
return ret;
}

// Parse options of the form "old;new[;extra]".
static std::tuple<StringRef, StringRef, StringRef>
getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) {
auto [oldDir, second] = getOldNewOptions(args, id);
auto [newDir, extraDir] = second.split(';');
return {oldDir, newDir, extraDir};
}

static StringRef getEntry(opt::InputArgList &args) {
auto *arg = args.getLastArg(OPT_entry, OPT_no_entry);
if (!arg) {
Expand Down Expand Up @@ -577,6 +604,24 @@ static void readConfigs(opt::InputArgList &args) {
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
std::tie(config->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew,
config->thinLTOPrefixReplaceNativeObject) =
getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace_eq);
if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) {
if (args.hasArg(OPT_thinlto_object_suffix_replace_eq))
error("--thinlto-object-suffix-replace is not supported with "
"--thinlto-emit-index-files");
else if (args.hasArg(OPT_thinlto_prefix_replace_eq))
error("--thinlto-prefix-replace is not supported with "
"--thinlto-emit-index-files");
}
if (!config->thinLTOPrefixReplaceNativeObject.empty() &&
config->thinLTOIndexOnlyArg.empty()) {
error("--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with "
"--thinlto-index-only=");
}
config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
config->whyExtract = args.getLastArgValue(OPT_why_extract);
errorHandler().verbose = args.hasArg(OPT_verbose);
Expand Down Expand Up @@ -721,7 +766,7 @@ static void checkOptions(opt::InputArgList &args) {
if (config->pie && config->shared)
error("-shared and -pie may not be used together");

if (config->outputFile.empty())
if (config->outputFile.empty() && !config->thinLTOIndexOnly)
error("no output file specified");

if (config->importTable && config->exportTable)
Expand Down
9 changes: 9 additions & 0 deletions lld/wasm/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ std::string toString(const wasm::InputFile *file) {

namespace wasm {

std::string replaceThinLTOSuffix(StringRef path) {
auto [suffix, repl] = config->thinLTOObjectSuffixReplace;
if (path.consume_back(suffix))
return (path + repl).str();
return std::string(path);
}

void InputFile::checkArch(Triple::ArchType arch) const {
bool is64 = arch == Triple::wasm64;
if (is64 && !config->is64) {
Expand Down Expand Up @@ -837,6 +844,8 @@ BitcodeFile::BitcodeFile(MemoryBufferRef m, StringRef archiveName,
this->archiveName = std::string(archiveName);

std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier());

// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
// name. If two archives define two members with the same name, this
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
// Opens a given file.
std::optional<MemoryBufferRef> readFile(StringRef path);

std::string replaceThinLTOSuffix(StringRef path);

} // namespace wasm

std::string toString(const wasm::InputFile *file);
Expand Down
13 changes: 11 additions & 2 deletions lld/wasm/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ using namespace llvm;
using namespace lld::wasm;
using namespace lld;

static std::string getThinLTOOutputFile(StringRef modulePath) {
return lto::getThinLTOOutputFile(modulePath, config->thinLTOPrefixReplaceOld,
config->thinLTOPrefixReplaceNew);
}

static lto::Config createConfig() {
lto::Config c;
c.Options = initTargetOptionsFromCodeGenFlags();
Expand Down Expand Up @@ -84,7 +89,10 @@ BitcodeCompiler::BitcodeCompiler() {
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
if (config->thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(config->thinLTOJobs), "", "", "",
llvm::hardware_concurrency(config->thinLTOJobs),
std::string(config->thinLTOPrefixReplaceOld),
std::string(config->thinLTOPrefixReplaceNew),
std::string(config->thinLTOPrefixReplaceNativeObject),
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
Expand Down Expand Up @@ -158,7 +166,8 @@ static void thinLTOCreateEmptyIndexFiles() {
continue;
if (linkedBitCodeFiles.contains(f->getName()))
continue;
std::string path(f->obj->getName());
std::string path =
replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName()));
std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
if (!os)
continue;
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ def thinlto_index_only: FF<"thinlto-index-only">;
def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
def thinlto_jobs: JJ<"thinlto-jobs=">,
HelpText<"Number of ThinLTO jobs. Default to --threads=">;
def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">;
def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
def lto_debug_pass_manager: FF<"lto-debug-pass-manager">,
HelpText<"Debug new pass manager">;

Expand Down

0 comments on commit b70eb86

Please sign in to comment.