Skip to content

[lld][WebAssembly] Implement various thinlto flags #114327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lld/test/wasm/lto/Inputs/thinlto_empty.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
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"
94 changes: 94 additions & 0 deletions lld/test/wasm/lto/obj-path.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
;; Copied from testr/ELF/lto/obj-path.ll
;; Test --lto-obj-path= for regular LTO.

; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: mkdir d
; RUN: opt 1.ll -o 1.bc
; RUN: opt 2.ll -o d/2.bc

; RUN: rm -f objpath.o
; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM
; RUN: llvm-objdump -d objpath.o | FileCheck %s
; RUN: ls 3* objpath* | count 2

; RUN: rm -f 3 objpath.o
; RUN: wasm-ld --thinlto-index-only=3.txt --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-objdump -d objpath.o | FileCheck %s
; RUN: not ls 3

; NM: T f
; NM: T g

; CHECK: file format wasm
; CHECK: <f>:
; CHECK: <g>:

;; Test --lto-obj-path= for ThinLTO.
; RUN: opt -module-summary 1.ll -o 1.bc
; RUN: opt -module-summary 2.ll -o d/2.bc

; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM3
; RUN: llvm-objdump -d objpath.o1 | FileCheck %s --check-prefix=CHECK1
; RUN: llvm-objdump -d objpath.o2 | FileCheck %s --check-prefix=CHECK2

; NM3: T f
; NM3-NEXT: T g

; CHECK1: file format wasm
; CHECK1-EMPTY:
; CHECK1-NEXT: Disassembly of section CODE:
; CHECK1: <f>:
; CHECK1-EMPTY:
; CHECK1-NEXT: end
; CHECK1-NOT: {{.}}

; CHECK2: file format wasm
; CHECK2-EMPTY:
; CHECK2-NEXT: Disassembly of section CODE:
; CHECK2: <g>:
; CHECK2-EMPTY:
; CHECK2-NEXT: end
; CHECK2-NOT: {{.}}

;; With --thinlto-index-only, --lto-obj-path= creates just one file.
; RUN: rm -f objpath.o objpath.o1 objpath.o2
; RUN: wasm-ld --thinlto-index-only --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o /dev/null
; RUN: llvm-objdump -d objpath.o | FileCheck %s --check-prefix=EMPTY
; RUN: not ls objpath.o1
; RUN: not ls objpath.o2

;; Ensure lld emits empty combined module if specific obj-path.
; RUN: mkdir obj
; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o obj/out --save-temps
; RUN: ls obj/out.lto.o out.lto.1.o d/out.lto.2.o

;; Ensure lld does not emit empty combined module by default.
; RUN: rm -fr obj && mkdir obj
; RUN: wasm-ld -shared 1.bc d/2.bc -o obj/out --save-temps
; RUN: not test -e obj/out.lto.o

; EMPTY: file format wasm
; EMPTY-NOT: {{.}}

;--- 1.ll
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
}

;--- 2.ll
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 @g() {
entry:
ret void
}
10 changes: 5 additions & 5 deletions lld/test/wasm/lto/parallel.ll
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
; RUN: llvm-as -o %t.bc %s
; RUN: rm -f %t.lto.o %t1.lto.o
; RUN: wasm-ld --lto-partitions=2 -save-temps -o %t %t.bc -r
; RUN: llvm-nm %t.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t1.lto.o | FileCheck --check-prefix=CHECK1 %s
; RUN: rm -rf %t && mkdir %t && cd %t
; RUN: llvm-as -o a.bc %s
; RUN: wasm-ld --lto-partitions=2 -save-temps -o out a.bc -r
; RUN: llvm-nm out.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm out.lto.1.o | FileCheck --check-prefix=CHECK1 %s

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-wasm"
Expand Down
145 changes: 145 additions & 0 deletions lld/test/wasm/lto/thinlto-index-only.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: mkdir d

;; First ensure that the ThinLTO handling in lld handles
;; bitcode without summary sections gracefully and generates index file.
; RUN: llvm-as 1.ll -o 1.o
; RUN: llvm-as %p/Inputs/thinlto.ll -o d/2.o
; RUN: wasm-ld --thinlto-index-only -shared 1.o d/2.o -o 3
; RUN: ls d/2.o.thinlto.bc
; RUN: not test -e 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: llvm-as 0.ll -o 0.o
; RUN: opt -module-summary 1.ll -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 doesn't generates index files when --thinlto-index-only is not enabled.
; RUN: rm -f 1.o.thinlto.bc d/2.o.thinlto.bc
; RUN: wasm-ld -shared 1.o d/2.o -o /dev/null
; RUN: not ls 1.o.thinlto.bc
; RUN: not ls d/2.o.thinlto.bc

;; Ensure lld generates an index and not a binary if requested.
; RUN: wasm-ld --thinlto-index-only -shared 1.o --start-lib d/2.o 3.o --end-lib 4.o -o 4
; RUN: not test -e 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

; RUN: rm -f 1.o.thinlto.bc d/2.o.thinlto.bc 3.o.thinlto.bc 4.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only=4.txt --thinlto-emit-imports-files -shared 1.o --start-lib d/2.o 3.o --end-lib 4.o -o 4
; RUN: not test -e 4
; RUN: FileCheck %s --check-prefix=RSP --implicit-check-not={{.}} < 4.txt
; 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
; RUN: FileCheck %s --check-prefix=IMPORTS1 --implicit-check-not={{.}} < 1.o.imports
; RUN: count 0 < d/2.o.imports
;; Test that LLD generates an empty index even for lazy object file that is not added to link.
; RUN: count 0 < 3.o.imports
; RUN: count 0 < 4.o.imports

;; Test interaction with --save-temps.
; RUN: rm -f 4.txt 1.o.thinlto.bc d/2.o.thinlto.bc 3.o.thinlto.bc 4.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only=4.txt --thinlto-emit-imports-files --save-temps -shared 0.o 1.o --start-lib d/2.o 3.o --end-lib 4.o -o t
; RUN: not test -e 4
; RUN: FileCheck %s --check-prefix=RSP --implicit-check-not={{.}} < 4.txt
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: FileCheck %s --check-prefix=IMPORTS1 --implicit-check-not={{.}} < 1.o.imports
; RUN: FileCheck %s --check-prefix=RESOLUTION < t.resolution.txt
; RUN: llvm-dis < t.index.bc | FileCheck %s --check-prefix=INDEX-BC

; RSP: 1.o
; RSP-NEXT: d/2.o
; RSP-NEXT: 4.o

; IMPORTS1: d/2.o

; RESOLUTION: 0.o
; RESOLUTION-NEXT: -r=0.o,foo,px
; RESOLUTION-NEXT: 1.o

; INDEX-BC: ^0 = module: (path: "1.o", hash: (0, 0, 0, 0, 0))
; INDEX-BC-NEXT: ^1 = module: (path: "4.o", hash: (0, 0, 0, 0, 0))
; INDEX-BC-NEXT: ^2 = module: (path: "d/2.o", hash: (0, 0, 0, 0, 0))

;; 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-unknown /dev/null -o dummy.o
; RUN: wasm-ld --thinlto-index-only -shared dummy.o --start-lib 1.o --end-lib -o /dev/null
; RUN: ls 1.o.thinlto.bc

;; Ensure when the same bitcode object is given as both lazy and non-lazy,
;; LLD does not generate an empty index for the lazy object.
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared 1.o d/2.o --start-lib d/2.o --end-lib -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared --start-lib d/2.o --end-lib d/2.o 1.o -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'

;; Ensure when the same lazy bitcode object is given multiple times,
;; no empty index file is generated if one of the copies is linked.
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared 1.o --start-lib d/2.o --end-lib --start-lib d/2.o --end-lib -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'

; 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))

;--- 0.ll
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 @foo() {
ret void
}

;--- 1.ll
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
}
67 changes: 35 additions & 32 deletions lld/test/wasm/lto/thinlto.ll
Original file line number Diff line number Diff line change
@@ -1,53 +1,56 @@
; Basic ThinLTO tests.
; RUN: opt -module-summary %s -o %t1.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t2.o
; RUN: rm -rf %t && mkdir %t && cd %t
; RUN: mkdir d e

; RUN: opt -module-summary %s -o a.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o d/b.o

; First force single-threaded mode
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=1 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=1 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Next force multi-threaded mode
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

;; --thinlto-jobs= defaults to --threads=.
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --threads=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --threads=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

;; --thinlto-jobs= overrides --threads=.
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --threads=1 --thinlto-jobs=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --threads=1 --thinlto-jobs=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with all threads, on all cores, on all CPU sockets
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=all %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=all a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with many more threads than the system has
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=100 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=100 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with a bad value
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: not wasm-ld -r -save-temps --thinlto-jobs=foo %t1.o %t2.o -o %t3 2>&1 | FileCheck %s --check-prefix=BAD-JOBS
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: not wasm-ld -r -save-temps --thinlto-jobs=foo a.o d/b.o -o e/out 2>&1 | FileCheck %s --check-prefix=BAD-JOBS
; BAD-JOBS: error: --thinlto-jobs: invalid job count: foo

; Check without --thinlto-jobs (which currently defaults to heavyweight_hardware_concurrency, meanning one thread per hardware core -- not SMT)
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; NM1: T f
; NM2: T g
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_lld_library(lldWasm
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
BitWriter
Core
Demangle
LTO
Expand Down
8 changes: 7 additions & 1 deletion lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ struct Configuration {
// Because dyamanic linking under Wasm is still experimental we default to
// static linking
bool isStatic = true;
bool thinLTOEmitImportsFiles;
bool thinLTOEmitIndexFiles;
bool thinLTOIndexOnly;
bool trace;
uint64_t globalBase;
uint64_t initialHeap;
Expand All @@ -95,16 +98,18 @@ struct Configuration {
unsigned ltoo;
llvm::CodeGenOptLevel ltoCgo;
unsigned optimize;
llvm::StringRef thinLTOJobs;
bool ltoDebugPassManager;
UnresolvedPolicy unresolvedSymbols;
BuildIdKind buildId = BuildIdKind::None;

llvm::StringRef entry;
llvm::StringRef ltoObjPath;
llvm::StringRef mapFile;
llvm::StringRef outputFile;
llvm::StringRef soName;
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOJobs;
llvm::StringRef thinLTOIndexOnlyArg;
llvm::StringRef whyExtract;

llvm::StringSet<> allowUndefinedSymbols;
Expand All @@ -126,6 +131,7 @@ struct Ctx {
llvm::SmallVector<StubFile *, 0> stubFiles;
llvm::SmallVector<SharedFile *, 0> sharedFiles;
llvm::SmallVector<BitcodeFile *, 0> bitcodeFiles;
llvm::SmallVector<BitcodeFile *, 0> lazyBitcodeFiles;
llvm::SmallVector<InputFunction *, 0> syntheticFunctions;
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
llvm::SmallVector<InputTable *, 0> syntheticTables;
Expand Down
Loading
Loading