Repository navigation
wasm: enable browser LTO and qualify JS SIMD profiles #2744
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -euo pipefail | ||
| cd "$(dirname "${BASH_SOURCE[0]}")/.." | ||
|
|
||
| : "${LLGO:=llgo}" | ||
| export GOEXPERIMENT=simd | ||
|
|
||
| for profile in gojs emscripten emscripten-memory64; do | ||
| ( | ||
| # Avoid inheriting a host target from the caller for raw GoJS builds. | ||
| export GOOS=js GOARCH=wasm CGO_ENABLED=0 | ||
| target=(-emulator) | ||
| if [[ "$profile" != gojs ]]; then | ||
| target+=(-target "$profile") | ||
| fi | ||
| echo "SIMD: $profile O0 boundary" | ||
| # The full O0 test binary exceeds Node's per-function local-variable limit. | ||
| "$LLGO" run -O0 "${target[@]}" ./test/simd/testdata/boundary | ||
| for lto in off thin full; do | ||
| echo "SIMD: $profile O2 LTO=$lto" | ||
| flags=(-O2) | ||
| if [[ "$lto" != off ]]; then | ||
| flags+=("-lto=$lto") | ||
| fi | ||
| "$LLGO" test "${flags[@]}" "${target[@]}" \ | ||
| -v -count=1 -timeout=2m -pclntab=none ./test/simd/... | ||
| done | ||
| for lto in thin full; do | ||
| echo "SIMD: $profile O3 LTO=$lto boundary" | ||
| # O3 argument promotion must preserve the JS SjLj memory bridge. | ||
| "$LLGO" run -O3 "-lto=$lto" "${target[@]}" ./test/simd/testdata/boundary | ||
| done | ||
| ) | ||
| done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2959,6 +2959,9 @@ func (c *context) archiver() string { | |
| if ar := os.Getenv("LLGO_AR"); ar != "" { | ||
| return ar | ||
| } | ||
| if ar := c.emscriptenArchiver(); ar != "" { | ||
| return ar | ||
| } | ||
| // First check toolchain directory (for cross-compilation) | ||
| if llvmAr := siblingTool(c.crossCompile.CC, "llvm-ar"); llvmAr != "" { | ||
| return llvmAr | ||
|
|
@@ -2979,6 +2982,9 @@ func (c *context) archiveMerger() (string, error) { | |
| if ar := os.Getenv("LLGO_AR"); ar != "" { | ||
| return ar, nil | ||
| } | ||
| if ar := c.emscriptenArchiver(); ar != "" { | ||
| return ar, nil | ||
| } | ||
| if llvmAr := siblingTool(c.crossCompile.CC, "llvm-ar"); llvmAr != "" { | ||
| return llvmAr, nil | ||
| } | ||
|
|
@@ -2988,6 +2994,27 @@ func (c *context) archiveMerger() (string, error) { | |
| return "", errors.New("llvm-ar is required to create a flat c-archive") | ||
| } | ||
|
|
||
| // Both GoJS and the named Emscripten targets build C/C++ through emcc. That | ||
| // bitcode can be newer than LLGo's linked LLVM, so use the SDK's emar wrapper | ||
| // to select its matching llvm-ar for both objects and MRI merges. | ||
| func (c *context) emscriptenArchiver() string { | ||
| provider := c.crossCompile.WasmProvider | ||
| if provider != crosscompile.WasmProviderGoJS && provider != crosscompile.WasmProviderEmscripten { | ||
| return "" | ||
| } | ||
| if cc, err := exec.LookPath(c.crossCompile.CC); err == nil { | ||
| // LookPath handles the SDK's emar.bat via PATHEXT on Windows; | ||
| // siblingTool only handles native executables such as llvm-ar.exe. | ||
| if ar, err := exec.LookPath(filepath.Join(filepath.Dir(cc), "emar")); err == nil { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: this sibling-tool probe diverges from the existing |
||
| return ar | ||
| } | ||
| } | ||
| if ar, err := exec.LookPath("emar"); err == nil { | ||
| return ar | ||
| } | ||
| return "" | ||
| } | ||
|
|
||
| func siblingTool(compiler, name string) string { | ||
| if compiler == "" { | ||
| return "" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -889,6 +889,16 @@ func useWithGOARMAndToolchain(goos, goarch, goarm string, forceEspClang bool, le | |
| // before an overflow can overwrite another goroutine's memory. | ||
| "-sSTACK_OVERFLOW_CHECK=2", | ||
| }...) | ||
| if ltoMode.Enabled() { | ||
| export.CCFLAGS = append(export.CCFLAGS, ltoMode.ClangFlag()) | ||
| export.LDFLAGS = append(export.LDFLAGS, ltoMode.ClangFlag()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor (consistency): this forwards only the base |
||
| // emcc's -O controls its compile and post-link pipelines, but can | ||
| // leave wasm-ld's LTO optimizer at its default O2 (e.g. SDK 6.0.8). | ||
| // Set the LTO level explicitly, as for the WASI linker above. | ||
| if optFlag := ltoLinkerOptFlag(level); optFlag != "" { | ||
| export.LDFLAGS = append(export.LDFLAGS, "-Wl,"+optFlag) | ||
| } | ||
| } | ||
| appendEmscriptenLibffiSearchPath(&export, llgoRoot, wasmProfile) | ||
| default: | ||
| err = errors.New("unsupported GOOS for WebAssembly: " + goos) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor (perf, pre-existing pattern amplified):
archiver()/archiveMerger()are called once per package increateArchiveFile, and on wasm/Emscripten buildsemscriptenArchiver()now adds up to ~3exec.LookPathPATH scans per package (CC, siblingemar, bareemar) on top of the existingllvm-arscan. The result is deterministic for the lifetime of thecontext. Consider resolving the archiver once and caching it (guarded withsync.Once, mirroring the existingplan9asmOnce) to avoid hundreds of redundant PATH scans on large builds. Not blocking.