Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
40 changes: 40 additions & 0 deletions .github/actions/setup-binaryen/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: "Setup Binaryen"
description: "Install a pinned Binaryen release"
inputs:
version:
description: "Binaryen release version"
required: false
default: "131"

runs:
using: "composite"
steps:
- name: Install Binaryen
shell: bash
run: |
set -euo pipefail

version="${{ inputs.version }}"
case "$(uname -s):$(uname -m)" in
Linux:x86_64) platform="x86_64-linux" ;;
Linux:aarch64|Linux:arm64) platform="aarch64-linux" ;;
Darwin:x86_64) platform="x86_64-macos" ;;
Darwin:arm64) platform="arm64-macos" ;;
*)
echo "Unsupported Binaryen host: $(uname -s) $(uname -m)" >&2
exit 1
;;
esac

archive="binaryen-version_${version}-${platform}.tar.gz"
base_url="https://github.com/WebAssembly/binaryen/releases/download/version_${version}"
cd "$RUNNER_TEMP"
curl --retry 3 --retry-all-errors -fsSLO "${base_url}/${archive}"
curl --retry 3 --retry-all-errors -fsSLO "${base_url}/${archive}.sha256"
if command -v sha256sum >/dev/null; then
sha256sum --check "${archive}.sha256"
else
shasum -a 256 --check "${archive}.sha256"
fi
tar -xzf "$archive" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/binaryen-version_${version}/bin" >> "$GITHUB_PATH"
3 changes: 3 additions & 0 deletions .github/workflows/build-cache.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
- name: Set up Go
uses: ./.github/actions/setup-go

- name: Set up Binaryen
uses: ./.github/actions/setup-binaryen

- name: Install wamr (for wasm tests)
if: startsWith(matrix.os, 'macos')
run: |
Expand Down
72 changes: 71 additions & 1 deletion .github/workflows/llgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ jobs:
- name: Set up Go for building llgo
uses: ./.github/actions/setup-go

- name: Set up Binaryen
uses: ./.github/actions/setup-binaryen

- name: Install wamr
run: |
git clone --branch WAMR-2.4.4 --depth 1 https://github.com/bytecodealliance/wasm-micro-runtime.git
Expand Down Expand Up @@ -374,6 +377,24 @@ jobs:
with:
version: "4.0.21"

- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: "25"

- name: Set up Binaryen
uses: ./.github/actions/setup-binaryen

- name: Set up Wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "39.0.1"

- name: Set up wasm-tools
uses: bytecodealliance/actions/wasm-tools/setup@v1
with:
version: "1.243.0"

- name: Set up Go for building llgo
uses: ./.github/actions/setup-go

Expand All @@ -390,6 +411,55 @@ jobs:
- name: Build standard runtime for wasm
shell: bash
run: |
run_wasm_scheduler() {
local module="$1"
local output
node --input-type=module -e "import Module from '$module'; await Module();"
if output=$(node --input-type=module -e "import Module from '$module'; await Module({preRun: [module => { module.ENV.LLGO_WASM_SCHEDULER_DEADLOCK = '1'; }]});" 2>&1); then
echo "deadlock scheduler fixture unexpectedly succeeded"
return 1
fi
grep -Fq "fatal error: all goroutines are asleep - deadlock!" <<<"$output"
if output=$(node --input-type=module -e "import Module from '$module'; await Module({preRun: [module => { module.ENV.LLGO_WASM_SCHEDULER_MAIN_GOEXIT = '1'; }]});" 2>&1); then
echo "main Goexit scheduler fixture unexpectedly succeeded"
return 1
fi
grep -Fq "WORKER_RETURNING" <<<"$output"
grep -Fq "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" <<<"$output"
}

run_wasi_scheduler() {
local module="$1"
local output
wasm-tools validate --features all "$module"
output=$(wasmtime run -W exceptions=y "$module" 2>&1)
grep -Fq "wasm scheduler ok" <<<"$output"
if output=$(wasmtime run -W exceptions=y \
--env LLGO_WASM_SCHEDULER_DEADLOCK=1 "$module" 2>&1); then
echo "deadlock scheduler fixture unexpectedly succeeded"
return 1
fi
grep -Fq "fatal error: all goroutines are asleep - deadlock!" <<<"$output"
if output=$(wasmtime run -W exceptions=y \
--env LLGO_WASM_SCHEDULER_MAIN_GOEXIT=1 "$module" 2>&1); then
echo "main Goexit scheduler fixture unexpectedly succeeded"
return 1
fi
grep -Fq "WORKER_RETURNING" <<<"$output"
grep -Fq "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" <<<"$output"
}

GOOS=js GOARCH=wasm llgo build -o "$RUNNER_TEMP/runtime-js.wasm" ./internal/build/testdata/wasm-runtime
GOOS=wasip1 GOARCH=wasm llgo build -o "$RUNNER_TEMP/runtime-wasip1.wasm" ./internal/build/testdata/wasm-runtime
file "$RUNNER_TEMP/runtime-js.wasm" "$RUNNER_TEMP/runtime-wasip1.wasm"
LLGO_WASI_THREADS=1 GOOS=wasip1 GOARCH=wasm llgo build \
-o "$RUNNER_TEMP/runtime-wasip1-threads.wasm" ./internal/build/testdata/wasm-runtime
test "$(wasmtime run -W exceptions=y "$RUNNER_TEMP/runtime-wasip1.wasm" 2>&1)" = "wasip1"
GOOS=js GOARCH=wasm llgo build -o "$RUNNER_TEMP/wasm-scheduler-go.mjs" ./internal/build/testdata/wasm-scheduler
run_wasm_scheduler "$RUNNER_TEMP/wasm-scheduler-go.mjs"
llgo build -target wasm -o "$RUNNER_TEMP/wasm-scheduler.mjs" ./internal/build/testdata/wasm-scheduler
run_wasm_scheduler "$RUNNER_TEMP/wasm-scheduler.mjs"
GOOS=wasip1 GOARCH=wasm llgo build -o "$RUNNER_TEMP/wasm-scheduler-wasip1.wasm" ./internal/build/testdata/wasm-scheduler
run_wasi_scheduler "$RUNNER_TEMP/wasm-scheduler-wasip1.wasm"
file "$RUNNER_TEMP/runtime-js.wasm" \
"$RUNNER_TEMP/runtime-wasip1.wasm" \
"$RUNNER_TEMP/runtime-wasip1-threads.wasm"
5 changes: 5 additions & 0 deletions .github/workflows/targets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ jobs:
with:
llvm-version: ${{matrix.llvm}}

- name: Set up Emscripten
uses: emscripten-core/setup-emsdk@v15
with:
version: "4.0.21"

- name: Set up Go for build
uses: ./.github/actions/setup-go

Expand Down
38 changes: 22 additions & 16 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ func resolveBuildConfig(input *Config) (*Config, error) {
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
if conf.AppExt == "" {
conf.AppExt = defaultAppExt(conf)
}
if conf.BuildMode == "" {
conf.BuildMode = BuildModeExe
}
Expand Down Expand Up @@ -399,6 +396,9 @@ func Build(inv Invocation) ([]Package, error) {
if conf.Target != "" && export.GOARCH != "" {
conf.Goarch = export.GOARCH
}
if conf.AppExt == "" {
conf.AppExt = defaultAppExt(conf)
}
if err := validateLinkOptions(conf, &export); err != nil {
return nil, err
}
Expand Down Expand Up @@ -479,10 +479,7 @@ func Build(inv Invocation) ([]Package, error) {
// final-PC sites for sidecar construction.
prog.EnableFuncInfoSites(shouldEnablePCLNSites(conf, funcInfo, emitDebugInfo))
sizes := func(sizes types.Sizes, compiler, arch string) types.Sizes {
if arch == "wasm" {
sizes = &types.StdSizes{WordSize: 4, MaxAlign: 4}
}
return prog.TypeSizes(sizes)
return prog.TypeSizes(effectiveTypeSizes(sizes, conf.Goos, arch, conf.Target))
}
dedup := packages.NewDeduper()
var syntaxErr error
Expand Down Expand Up @@ -793,16 +790,22 @@ func DefaultBuildTags(goarch, target string) string {

func defaultBuildTags(goarch, target string) string {
tags := "llgo,math_big_pure_go,purego"
// Raw GOOS/GOARCH wasm builds do not have a target configuration that
// selects a collector. BDWGC is not available in either wasm host, so use
// the supported collector-free runtime unless a named target supplies its
// own runtime configuration.
if goarch == "wasm" && target == "" {
// BDWGC is unavailable in both wasm hosts.
if goarch == "wasm" {
tags += ",nogc"
}
return tags
}

func effectiveTypeSizes(sizes types.Sizes, goos, goarch, target string) types.Sizes {
// Named wasm targets use the native wasm32 data model. The raw js/wasm
// entry point keeps Go's 64-bit word model and is emitted as Memory64.
if goarch == "wasm" && (target != "" || goos != "js") {
return &types.StdSizes{WordSize: 4, MaxAlign: 4}
}
return sizes
}

func allowMissingFunctionBodies(initial []*packages.Package) {
for _, pkg := range initial {
hasMissingBody := false
Expand Down Expand Up @@ -1497,12 +1500,15 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
}
linkArgs = append(linkArgs, cSharedExportArgs(ctx, linkedOrder)...)

err = linkObjFiles(ctx, outputPath, linkInputs, linkArgs, verbose)
linkOutput, err := prepareWasmLinkOutput(ctx.buildConf, &ctx.crossCompile, outputPath)
if err != nil {
return err
}

return nil
defer cleanupWasmLinkOutput(linkOutput, outputPath)
if err := linkObjFiles(ctx, linkOutput, linkInputs, linkArgs, verbose); err != nil {
return err
}
return publishWasmLinkOutput(ctx, linkOutput, outputPath, verbose)
}

func linkedPackageMetas(pkgs []Package) []*meta.PackageMeta {
Expand Down Expand Up @@ -2681,7 +2687,7 @@ func shouldRunLLVMPasses(mode Mode) bool {
}

func IsWasiThreadsEnabled() bool {
return isEnvOn(llgoWasiThreads, true)
return isEnvOn(llgoWasiThreads, false)
}

func IsFullRpathEnabled() bool {
Expand Down
37 changes: 36 additions & 1 deletion internal/build/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func TestDefaultBuildTags(t *testing.T) {
}{
{name: "native", goarch: "arm64", want: base},
{name: "raw wasm", goarch: "wasm", want: base + ",nogc"},
{name: "configured wasm target", goarch: "wasm", target: "wasip1", want: base},
{name: "configured wasm target", goarch: "wasm", target: "wasip1", want: base + ",nogc"},
} {
t.Run(test.name, func(t *testing.T) {
if got := defaultBuildTags(test.goarch, test.target); got != test.want {
Expand All @@ -342,6 +342,30 @@ func TestDefaultBuildTags(t *testing.T) {
}
}

func TestEffectiveWasmTypeSizes(t *testing.T) {
goSizes := types.SizesFor("gc", "wasm")
for _, test := range []struct {
name string
goos string
target string
want int64
}{
{name: "Go js wasm", goos: "js", want: 8},
{name: "configured wasm", goos: "js", target: "wasm", want: 4},
{name: "WASI compatibility", goos: "wasip1", want: 4},
} {
t.Run(test.name, func(t *testing.T) {
got := effectiveTypeSizes(goSizes, test.goos, "wasm", test.target)
if size := got.Sizeof(types.Typ[types.Uintptr]); size != test.want {
t.Fatalf("uintptr size = %d, want %d", size, test.want)
}
})
}
if got := effectiveTypeSizes(goSizes, "linux", "amd64", ""); got != goSizes {
t.Fatal("native type sizes changed")
}
}

func TestWasmRuntimeAvoidsNativeHostDependencies(t *testing.T) {
runtimeDir := filepath.Join(env.LLGoRuntimeDir(), "internal", "lib", "runtime")
for _, goos := range []string{"js", "wasip1"} {
Expand Down Expand Up @@ -1182,6 +1206,17 @@ func TestApplyBuildModeCompileFlags(t *testing.T) {
applyBuildModeCompileFlags(BuildModeCShared, nil)
}

func TestWASIThreadsAreOptIn(t *testing.T) {
t.Setenv(llgoWasiThreads, "")
if IsWasiThreadsEnabled() {
t.Fatal("WASI threads are enabled by default")
}
t.Setenv(llgoWasiThreads, "1")
if !IsWasiThreadsEnabled() {
t.Fatal("WASI threads opt-in was ignored")
}
}

func TestCHeaderPackagesExcludesStandardRuntime(t *testing.T) {
prog := llssa.NewProgram(nil)
defer prog.Dispose()
Expand Down
39 changes: 34 additions & 5 deletions internal/build/main_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g
}

var rtInit llssa.Function
if cfg.rtInit {
if cfg.rtInit || ctx.crossCompile.WasmPostLink.Asyncify {
rtInit = declareNoArgFunc(mainPkg, rtPkgPath+".init")
}

Expand Down Expand Up @@ -133,10 +133,16 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g
return mainAPkg
}

var wasmRunMain llssa.Function
if ctx.crossCompile.WasmPostLink.Asyncify {
defineWasmMainTask(mainPkg, packageInits, mainInit, mainMain)
wasmRunMain = declareNoArgFunc(mainPkg, rtPkgPath+".RunWasmMain")
}
entryFn := defineEntryFunction(ctx, mainPkg, argcVar, argvVar, argvValueType, entryFunctions{
runtimeStub: runtimeStub,
mainInit: mainInit,
mainMain: mainMain,
wasmRunMain: wasmRunMain,
pyInit: pyInit,
pyFinalize: pyFinalize,
rtInit: rtInit,
Expand Down Expand Up @@ -232,6 +238,7 @@ type entryFunctions struct {
runtimeStub llssa.Function
mainInit llssa.Function
mainMain llssa.Function
wasmRunMain llssa.Function
pyInit llssa.Function
pyFinalize llssa.Function
rtInit llssa.Function
Expand Down Expand Up @@ -280,11 +287,15 @@ func defineEntryFunction(ctx *context, pkg llssa.Package, argcVar, argvVar llssa
b.Call(fns.abiInit.Expr)
}
b.Call(fns.runtimeStub.Expr)
for _, init := range fns.packageInits {
b.Call(init.Expr)
if fns.wasmRunMain != nil {
b.Call(fns.wasmRunMain.Expr)
} else {
for _, init := range fns.packageInits {
b.Call(init.Expr)
}
b.Call(fns.mainInit.Expr)
b.Call(fns.mainMain.Expr)
}
b.Call(fns.mainInit.Expr)
b.Call(fns.mainMain.Expr)
if fns.pyFinalize != nil {
b.Call(fns.pyFinalize.Expr)
}
Expand All @@ -295,6 +306,24 @@ func defineEntryFunction(ctx *context, pkg llssa.Package, argcVar, argvVar llssa
return fn
}

func defineWasmMainTask(pkg llssa.Package, packageInits []llssa.Function, mainInit, mainMain llssa.Function) {
prog := pkg.Prog
sig := newSignature(
[]types.Type{types.Typ[types.UnsafePointer]},
[]types.Type{types.Typ[types.UnsafePointer]},
)
fn := pkg.NewFunc("__llgo_wasm_main", sig, llssa.InC)
fnVal := pkg.Module().NamedFunction("__llgo_wasm_main")
fnVal.SetVisibility(llvm.HiddenVisibility)
b := fn.MakeBody(1)
for _, init := range packageInits {
b.Call(init.Expr)
}
b.Call(mainInit.Expr)
b.Call(mainMain.Expr)
b.Return(prog.Nil(prog.VoidPtr()))
}

func defineStart(pkg llssa.Package, entry llssa.Function, argvType llssa.Type) {
fn := pkg.NewFunc("_start", llssa.NoArgsNoRet, llssa.InC)
pkg.Module().NamedFunction("_start").SetLinkage(llvm.WeakAnyLinkage)
Expand Down
Loading
Loading