Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
cdfda07
Do not emit trailing whitespace on blank lines (#9052)
tlively Aug 28, 2026
a30696f
Emit CHECK-EMPTY directives in test update scripts (#9053)
tlively Aug 28, 2026
23c19e2
Update pinned filecheck version to 1.0.3 (#9054)
tlively Aug 28, 2026
8bad4db
Fast-math mode can ignore the difference between negative zero and ze…
kripken Aug 28, 2026
39be64e
Update to filecheck 1.0.5 (#9059)
tlively Aug 31, 2026
3a46a6c
[wasm-split] Don't use update_lit_checks.py for some tests (#9061)
aheejin Aug 31, 2026
f870c9e
ConstraintAnalysis: Fix a hang with a tee in an increment (#9058)
kripken Aug 31, 2026
6531a98
[wasm-split] Make mutable/immutable versions of tests (#9062)
aheejin Sep 1, 2026
d036a3b
Add missing visit to descriptor in LinearExecutionWalker (#9060)
kripken Sep 1, 2026
3d7eee7
Increase ConstraintAnalysis limit on basic block computations (#9065)
kripken Sep 1, 2026
2048605
Fix typos in the codebase (#9063)
aheejin Sep 2, 2026
0f19a05
OptimizeInstructions: Do not remove an inner cast when there are desc…
kripken Sep 2, 2026
e7a4551
Constraints: Add comparisons for term-equal proving (#9066)
kripken Sep 2, 2026
6c4214f
Require matching finality in descriptor types (#9068)
tlively Sep 2, 2026
180bae3
Add a monitor_fuzz.py script (#9069)
tlively Sep 2, 2026
c75f2ae
Add a suspends effect (#9070)
tlively Sep 3, 2026
cdb9eaa
Use fast gzip compression level 1 in ClusterFuzz bundling (#9076)
brendandahl Sep 4, 2026
2abebd2
Initial support for `publish` (#9072)
tlively Sep 5, 2026
a7f7909
Store primitive GC arrays as raw byte buffers (#9051)
brendandahl Sep 8, 2026
72e6b56
[Stack Switching / Exceptions] Add missing null check for null exnref…
kripken Sep 9, 2026
8d786b3
[CI] Use existing clang, don't install using llvm.sh (#9087)
kripken Sep 9, 2026
ac79149
Add unified parallel test runner and refactor spec tests (#9083)
brendandahl Sep 9, 2026
d2e5e03
Parallelize wasm-opt passes and print tests (#9088)
brendandahl Sep 9, 2026
5d93aba
Fix fallthrough for desc operands (#9089)
tlively Sep 9, 2026
3e2bcc0
Optimize resume of non-suspending continuations (#9071)
tlively Sep 9, 2026
d57c2a8
ConstraintAnalysis: Parse AND operations (#9075)
kripken Sep 9, 2026
994a5cb
Avoid newly exposing prototypes in GTO (#9043)
tlively Sep 10, 2026
b872d11
Optimize tail-padding reuse [NFC] (#9084)
kripken Sep 10, 2026
ee09893
[NFC] Remove ancient trapping-mode support code (#9095)
kripken Sep 10, 2026
14fdf52
[NFC] Reorder some small fields in wasm.h for size reduction (#9094)
kripken Sep 10, 2026
14bdb7f
Parallelize wasm2js test suite (#9090)
brendandahl Sep 10, 2026
43dd294
[NFC] Process Binary arms in natural execution order in Constraints (…
kripken Sep 11, 2026
cb3586b
Parallelize native example testcases (#9097)
brendandahl Sep 11, 2026
45b6cd9
Optimize `publish` (#9091)
tlively Sep 11, 2026
1883e1b
Parallelize fixed wasm-reduce testcases (#9101)
brendandahl Sep 11, 2026
b7c5ff3
[ctor-eval] Fix partial evaluation of return calls (#9104)
tlively Sep 11, 2026
5eb87bf
Fix invalid tag index when parsing declarative element segments with …
ArkadySkv Sep 14, 2026
7d9ec2f
[BranchHinting] Do not reorder traps in OptimizeInstructions when nev…
kripken Sep 14, 2026
2746295
Support parallel fuzzing in monitor_fuzz.py (#9105)
tlively Sep 14, 2026
b71b29e
Port threaded tests from acquire/release proposal (#9107)
stevenfontanella Sep 15, 2026
1b198ae
Add a pass to remove empty function exports (#9078)
GtechGovind Sep 16, 2026
5b07da1
[GTO] Update struct field indices after localizing operands (#9108)
tlively Sep 16, 2026
0c068b7
Add TailCall optimization pass (--tail-call) (#9103)
tlively Sep 16, 2026
e653452
ConstraintAnalysis: Parse tees (#9109)
kripken Sep 16, 2026
cbecb31
[GUFA] Fix crash on switch handlers (#9110)
tlively Sep 16, 2026
7d27e49
[TailCall] Fix intrinsic and unreachable calls (#9111)
tlively Sep 17, 2026
3f54c9d
Optionally collect fuzzer statistics across runs (#9073)
tlively Sep 17, 2026
e45f5ba
Mark atomic read-write etc. operations as generative (#9112)
kripken Sep 17, 2026
829cbec
Fix TypeRefining-GUFA on unreachable StructCmpxchg (#9113)
kripken Sep 18, 2026
fbf2e5a
[MakeSharedObjects] Lower externrefs using a dynamically grown table …
tlively Sep 18, 2026
21312a0
[JS API] Organize types into enums (#9098)
chharvey Sep 18, 2026
da9a372
Update monitor_fuzz.py return codes (#9120)
tlively Sep 19, 2026
a2a6ba0
Merge branch 'main' into oscar/expose-new-exceptions
chharvey Sep 21, 2026
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
18 changes: 3 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ jobs:
lint:
name: lint
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
# Use Ubuntu 26.04 to get clang-21 (not present in 24.04)
runs-on: ubuntu-26.04
env:
# Keep this in sync with clang-format-diff.sh
LLVM_VERSION: 21
Expand All @@ -34,10 +35,7 @@ jobs:
run: |
pip3 install -r requirements-dev.txt
sudo apt install lsb-release wget software-properties-common gnupg
wget https://apt.llvm.org/llvm.sh
sudo chmod +x llvm.sh
sudo ./llvm.sh ${LLVM_VERSION}
sudo apt-get install clang-format clang-format-${LLVM_VERSION} clang-tidy-${LLVM_VERSION}
sudo apt-get install clang-${LLVM_VERSION} clang-format-${LLVM_VERSION} clang-tidy-${LLVM_VERSION}
- run: ruff check
- run: ./scripts/clang-format-diff.sh
- name: clang-tidy
Expand Down Expand Up @@ -199,11 +197,6 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: true
- name: install clang 18
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
- name: install ninja
run: sudo apt-get install ninja-build
- name: install v8
Expand Down Expand Up @@ -322,11 +315,6 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: true
- name: install clang 18
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
- name: install ninja
run: sudo apt-get install ninja-build
- name: install v8
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Current Trunk
- Replace the `BINARYEN_ROOT` environment variable (used by developers who are
doing out-of-tree builds of binaryen) with `BINARYEN_BIN` (#9023)
- Reject non-natural alignment for atomic memory operations at parse time (#8962)
- Note that fast-math mode can ignore the difference between negative zero and
zero (like clang and gcc). (#9056)
- [JS API] **(breaking)** Organize types into enums (#9098)

v132
----
Expand Down
173 changes: 69 additions & 104 deletions check.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@

import functools
import glob
import io
import os
import subprocess
import sys
import unittest
from contextlib import contextmanager
from multiprocessing.pool import ThreadPool
from pathlib import Path

from scripts.test import binaryenjs, finalize, shared, support, wasm2js, wasm_opt
Expand Down Expand Up @@ -140,6 +137,32 @@ def run_wasm_metadce_tests():
shared.fail_if_not_identical_to_file(stdout, expected + '.stdout')


def run_one_wasm_reduce_test(t, stdout=None):
base_name = os.path.splitext(os.path.basename(t))[0]
print('..', os.path.basename(t), file=stdout)
a_wasm = f'reduce_{base_name}_a.wasm'
b_wasm = f'reduce_{base_name}_b.wasm'
c_wasm = f'reduce_{base_name}_c.wasm'
a_wat = f'reduce_{base_name}_a.wat'
try:
support.run_command(shared.WASM_AS + [t, '-o', a_wasm, '-all'], stdout=stdout)
cmd = shared.WASM_OPT[0]
support.run_command(
shared.WASM_REDUCE + [a_wasm, f'--command={cmd} {b_wasm} --fuzz-exec -all', '-t', b_wasm, '-w', c_wasm, '--timeout=4'],
stdout=stdout,
stderr=subprocess.PIPE,
)
expected = t + '.txt'
support.run_command(shared.WASM_DIS + [c_wasm, '-o', a_wat], stdout=stdout)
with open(a_wat) as seen:
shared.fail_if_not_identical_to_file(seen.read(), expected)
finally:
shared.delete_from_orbit(a_wasm)
shared.delete_from_orbit(b_wasm)
shared.delete_from_orbit(c_wasm)
shared.delete_from_orbit(a_wat)


def run_wasm_reduce_tests():
if not shared.has_shell_timeout():
print_heading('skipping wasm-reduce testcases')
Expand All @@ -148,16 +171,8 @@ def run_wasm_reduce_tests():
print_heading('checking wasm-reduce testcases')

# fixed testcases
for t in shared.get_tests(shared.get_test_dir('reduce'), ['.wast']):
print('..', os.path.basename(t))
# convert to wasm
support.run_command(shared.WASM_AS + [t, '-o', 'a.wasm', '-all'])
cmd = shared.WASM_OPT[0]
support.run_command(shared.WASM_REDUCE + ['a.wasm', f'--command={cmd} b.wasm --fuzz-exec -all ', '-t', 'b.wasm', '-w', 'c.wasm', '--timeout=4'])
expected = t + '.txt'
support.run_command(shared.WASM_DIS + ['c.wasm', '-o', 'a.wat'])
with open('a.wat') as seen:
shared.fail_if_not_identical_to_file(seen.read(), expected)
tests = shared.get_tests(shared.get_test_dir('reduce'), ['.wast'])
shared.run_parallel_tests(run_one_wasm_reduce_test, tests)

# run on a nontrivial fuzz testcase, for general coverage
# this is very slow in ThreadSanitizer, so avoid it there
Expand Down Expand Up @@ -232,7 +247,6 @@ def run_one_spec_test(wast: Path, stdout=None):
shared.verbose_log('<< test failed as expected >>', file=stdout)
return # don't try all the binary format stuff TODO
else:
shared.fail_with_error(str(e))
raise

check_expected(actual, expected, stdout=stdout)
Expand Down Expand Up @@ -265,67 +279,10 @@ def run_one_spec_test(wast: Path, stdout=None):
check_expected(actual, os.path.join(shared.get_test_dir('spec'), 'expected-output', test_name + '.log'), stdout=stdout)


def run_spec_test_with_wrapped_stdout(wast: Path):
"""Run a single spec test while capturing stdout.

Return (bool, str) where the first element is whether the test was
successful and the second is the combined stdout and stderr of the test.
"""
out = io.StringIO()
try:
run_one_spec_test(wast, stdout=out)
except Exception as e:
shared.num_failures += 1
# Serialize exceptions into the output string buffer
# so they can be reported on the main thread.
print(e, file=out)
return False, out.getvalue()
return True, out.getvalue()


@contextmanager
def red_output(file=sys.stdout):
print("\033[31m", end="", file=file)
try:
yield
finally:
print("\033[0m", end="", file=file)


def red_stderr():
return red_output(file=sys.stderr)


def run_spec_tests():
print_heading('checking wasm-shell spec testcases...')

worker_count = os.cpu_count()
print("Running with", worker_count, "workers")
test_paths = (Path(x) for x in shared.options.spec_tests)

failed_stdouts = []
with ThreadPool(processes=worker_count) as pool:
try:
for success, stdout in pool.imap_unordered(run_spec_test_with_wrapped_stdout, test_paths):
if success:
print(stdout, end="")
continue

failed_stdouts.append(stdout)
if shared.options.abort_on_first_failure:
with red_stderr():
print("Aborted spec test suite execution after first failure. Set --no-fail-fast to disable this.", file=sys.stderr)
break
except KeyboardInterrupt:
# Hard exit to avoid threads continuing to run after Ctrl-C.
# There's no concern of deadlocking during shutdown here.
os._exit(1)

if failed_stdouts:
with red_stderr():
print("Failed tests:", file=sys.stderr)
for failed in failed_stdouts:
print(failed, end="", file=sys.stderr)
shared.run_parallel_tests(run_one_spec_test, test_paths)


def run_validator_tests():
Expand All @@ -345,6 +302,44 @@ def run_validator_tests():
support.run_command(cmd, expected_status=1)


def run_one_example_test(t, stdout=None):
src = os.path.join(shared.get_test_dir('example'), t)
expected = os.path.join(shared.get_test_dir('example'), '.'.join(t.split('.')[:-1]) + '.txt')
# build the C file separately
libpath = shared.options.binaryen_lib
output_file = os.path.basename(os.path.splitext(t)[0])
objfile = output_file + '.o'
compile = [shared.NATIVECC, src, '-c', '-o', objfile,
'-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-L' + libpath, '-pthread']
if src.endswith('.cpp'):
compile += ['-std=c++' + str(shared.cxx_standard)]
if os.environ.get('COMPILER_FLAGS'):
for f in os.environ.get('COMPILER_FLAGS').split(' '):
compile.append(f)
print('build: ', ' '.join(compile), file=stdout)
proc = subprocess.run(compile, capture_output=True, text=True)
if proc.returncode != 0:
raise Exception(f"Failed to compile {src}:\n{proc.stderr or proc.stdout}")

cmd = ['-I' + os.path.join(shared.options.binaryen_root, 't'), '-g', '-pthread', '-o', output_file]
# Link against the binaryen C library DSO, using an executable-relative rpath
cmd = [objfile, '-L' + libpath, '-lbinaryen'] + cmd + ['-Wl,-rpath,' + libpath]
print(' ', t, src, expected, file=stdout)
if os.environ.get('COMPILER_FLAGS'):
for f in os.environ.get('COMPILER_FLAGS').split(' '):
cmd.append(f)
cmd = [shared.NATIVEXX, '-std=c++' + str(shared.cxx_standard)] + cmd
print('link: ', ' '.join(cmd), file=stdout)
proc = subprocess.run(cmd, capture_output=True, text=True)
if proc.returncode != 0:
raise Exception(f"Failed to link {output_file}:\n{proc.stderr or proc.stdout}")
print('run...', output_file, file=stdout)
proc = subprocess.run([os.path.abspath(output_file)], capture_output=True, text=True)
if proc.returncode != 0:
raise Exception(f"Failed to run {output_file}:\n{proc.stderr or proc.stdout}")
shared.fail_if_not_identical_to_file(proc.stdout, expected)


def run_example_tests():
print_heading('checking native example testcases...')
if not shared.NATIVECC or not shared.NATIVEXX:
Expand All @@ -354,38 +349,8 @@ def run_example_tests():
if shared.skip_if_on_windows('example'):
return

for t in shared.get_tests(shared.get_test_dir('example')):
if not t.endswith(('.c', '.cpp')):
continue
src = os.path.join(shared.get_test_dir('example'), t)
expected = os.path.join(shared.get_test_dir('example'), '.'.join(t.split('.')[:-1]) + '.txt')
# build the C file separately
libpath = shared.options.binaryen_lib
output_file = os.path.basename(os.path.splitext(t)[0])
objfile = output_file + '.o'
compile = [shared.NATIVECC, src, '-c', '-o', objfile,
'-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-L' + libpath, '-pthread']
if src.endswith('.cpp'):
compile += ['-std=c++' + str(shared.cxx_standard)]
if os.environ.get('COMPILER_FLAGS'):
for f in os.environ.get('COMPILER_FLAGS').split(' '):
compile.append(f)
print('build: ', ' '.join(compile))
subprocess.check_call(compile)

cmd = ['-I' + os.path.join(shared.options.binaryen_root, 't'), '-g', '-pthread', '-o', output_file]
# Link against the binaryen C library DSO, using an executable-relative rpath
cmd = [objfile, '-L' + libpath, '-lbinaryen'] + cmd + ['-Wl,-rpath,' + libpath]
print(' ', t, src, expected)
if os.environ.get('COMPILER_FLAGS'):
for f in os.environ.get('COMPILER_FLAGS').split(' '):
cmd.append(f)
cmd = [shared.NATIVEXX, '-std=c++' + str(shared.cxx_standard)] + cmd
print('link: ', ' '.join(cmd))
subprocess.check_call(cmd)
print('run...', output_file)
actual = subprocess.check_output([os.path.abspath(output_file)], text=True)
shared.fail_if_not_identical_to_file(actual, expected)
tests = shared.get_tests(shared.get_test_dir('example'), ['.c', '.cpp'])
shared.run_parallel_tests(run_one_example_test, tests)


def run_unittest():
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# Install with `pip3 install -r requirements-dev.txt`

ruff==0.16.0
filecheck==0.0.22
filecheck==1.0.5
lit==0.11.0.post1
3 changes: 2 additions & 1 deletion scripts/bundle_clusterfuzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@
'--disable-relaxed-atomics',
]

with tarfile.open(output_file, "w:gz") as tar:
# Use fast compression (level 1) to speed up bundling with only a modest size increase.
with tarfile.open(output_file, 'w:gz', compresslevel=1) as tar:
# run.py
run = os.path.join(shared.options.binaryen_root, 'scripts', 'clusterfuzz', 'run.py')
print(f' .. run: {run}')
Expand Down
22 changes: 14 additions & 8 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,12 @@ def auto_select_recent_initial_contents():
# commit time of HEAD. The reason we use the commit time of HEAD instead
# of the current system time is to make the results deterministic given
# the Binaryen HEAD commit.
head_ts_str = run(['git', 'log', '-1', '--format=%cd', '--date=raw'],
head_ts_str = run(['git', '-C', shared.options.binaryen_root, 'log', '-1', '--format=%cd', '--date=raw'],
silent=True).split()[0]
head_dt = datetime.utcfromtimestamp(int(head_ts_str))
start_dt = head_dt - timedelta(days=RECENT_DAYS)
start_ts = start_dt.replace(tzinfo=timezone.utc).timestamp()
log = run(['git', 'log', '--name-status', '--format=', '--date=raw', '--no-renames', f'--since={start_ts}'], silent=True).splitlines()
log = run(['git', '-C', shared.options.binaryen_root, 'log', '--name-status', '--format=', '--date=raw', '--no-renames', f'--since={start_ts}'], silent=True).splitlines()
# Pick up lines in the form of
# A test/../something.wast
# M test/../something.wast
Expand All @@ -290,7 +290,7 @@ def auto_select_recent_initial_contents():

def is_git_repo():
try:
ret = run(['git', 'rev-parse', '--is-inside-work-tree'],
ret = run(['git', '-C', shared.options.binaryen_root, 'rev-parse', '--is-inside-work-tree'],
silent=True, stderr=subprocess.DEVNULL)
return ret == 'true\n'
except subprocess.CalledProcessError:
Expand Down Expand Up @@ -1008,7 +1008,7 @@ def __init__(self):
D8(),
D8Liftoff(),
D8Turboshaft(),
# FIXME: Temprorary disable. See issue #4741 for more details
# FIXME: Temporary disable. See issue #4741 for more details
# Wasm2C(),
# Wasm2C2Wasm()
]
Expand Down Expand Up @@ -1372,7 +1372,7 @@ def handle_pair(self, input, before_wasm, after_wasm, opts):
# "[fuzz-exec] export bar".
call_start = before.rfind(FUZZ_EXEC_EXPORT_PREFIX, 0, trap_index)
if call_start < 0:
# the trap happened before we called an export, so it occured
# the trap happened before we called an export, so it occurred
# during startup (the start function, or memory segment
# operations, etc.). in that case there is nothing for us to
# compare here; just leave.
Expand Down Expand Up @@ -2336,6 +2336,10 @@ def do_run(vm, js, wasm):
#
# Ignore it, as details of traces differ based on optimizations.
continue
elif not line:
# V8 may print blank lines before stack traces when the top
# frame has no script location (e.g. after a return_call to JS).
continue
cleaned.append(line)
cleaned = '\n'.join(cleaned)

Expand Down Expand Up @@ -2575,7 +2579,7 @@ def handle(self, wasm):
TrapsNeverHappen(),
CtorEval(),
Merge(),
# Split(), # Will reenable after stabilized
# Split(), # Will re-enable after stabilized
RoundtripText(),
ClusterFuzz(),
Two(),
Expand Down Expand Up @@ -2765,6 +2769,7 @@ def write_commands(commands, filename):
("--simplify-locals-notee",),
("--simplify-locals-notee-nostructure",),
("--ssa",),
("--tail-call",),
("--tuple-optimization",),
("--type-finalizing",),
("--type-refining",),
Expand Down Expand Up @@ -3014,6 +3019,7 @@ def get_random_opts():
working_wasm = abspath('w.wasm')
wasm_reduce = in_bin('wasm-reduce')
reduce_sh = abspath('reduce.sh')
fuzz_opt = in_binaryen('scripts', 'fuzz_opt.py')
features = ' '.join(FEATURE_OPTS)
with open('reduce.sh', 'w') as f:
f.write(f'''\
Expand All @@ -3026,12 +3032,12 @@ def get_random_opts():

if [ -z "$BINARYEN_FIRST_WASM" ]; then
# run the command normally
./scripts/fuzz_opt.py {auto_init} --binaryen-bin {binaryen_bin} {seed} {temp_wasm} > o 2> e
{fuzz_opt} {auto_init} --binaryen-bin {binaryen_bin} {seed} {temp_wasm} > o 2> e
else
# BINARYEN_FIRST_WASM was provided so we should actually reduce the *second*
# file. pass the first one in as the main file, and use the env var for the
# second.
BINARYEN_SECOND_WASM={temp_wasm} ./scripts/fuzz_opt.py {auto_init} --binaryen-bin {binaryen_bin} {seed} $BINARYEN_FIRST_WASM > o 2> e
BINARYEN_SECOND_WASM={temp_wasm} {fuzz_opt} {auto_init} --binaryen-bin {binaryen_bin} {seed} $BINARYEN_FIRST_WASM > o 2> e
fi

echo " " $?
Expand Down
Loading