@@ -1360,7 +1360,7 @@ def can_run_on_wasm(self, wasm):
13601360
13611361# Tests wasm-ctor-eval
13621362class CtorEval (TestCaseHandler ):
1363- frequency = 0.2
1363+ frequency = 0.1
13641364
13651365 def handle (self , wasm ):
13661366 # get the expected execution results.
@@ -1477,7 +1477,9 @@ def can_run_on_wasm(self, wasm):
14771477FUNC_NAMES_REGEX = re .compile (r'\n [(]func [$](\S+)' )
14781478
14791479
1480- # Tests wasm-split
1480+ # Tests wasm-split. This also tests that fuzz_shell.js properly executes 2 wasm
1481+ # files, which adds coverage for ClusterFuzz (which sometimes runs two wasm
1482+ # files in that way).
14811483class Split (TestCaseHandler ):
14821484 frequency = 1 # TODO: adjust lower when we actually enable this
14831485
@@ -1670,6 +1672,78 @@ def ensure(self):
16701672 tar .close ()
16711673
16721674
1675+ # Tests linking two wasm files at runtime, and that optimizations do not break
1676+ # anything. This is similar to Split(), but rather than split a wasm file into
1677+ # two and link them at runtime, this starts with two separate wasm files.
1678+ class Two (TestCaseHandler ):
1679+ frequency = 0.2
1680+
1681+ def handle (self , wasm ):
1682+ # Generate a second wasm file, unless we were given one (useful during
1683+ # reduction).
1684+ second_wasm = abspath ('second.wasm' )
1685+ given = os .environ .get ('BINARYEN_SECOND_WASM' )
1686+ if given :
1687+ # TODO: should we de-nan this etc. as with the primary?
1688+ shutil .copyfile (given , second_wasm )
1689+ else :
1690+ second_input = abspath ('second_input.dat' )
1691+ make_random_input (random_size (), second_input )
1692+ args = [second_input , '-ttf' , '-o' , second_wasm ]
1693+ run ([in_bin ('wasm-opt' )] + args + GEN_ARGS + FEATURE_OPTS )
1694+
1695+ # The binaryen interpreter only supports a single file, so we run them
1696+ # from JS using fuzz_shell.js's support for two files.
1697+ #
1698+ # Note that we *cannot* run each wasm file separately and compare those
1699+ # to the combined output, as fuzz_shell.js intentionally allows calls
1700+ # *between* the wasm files, through JS APIs like call-export*. So all we
1701+ # do here is see the combined, linked behavior, and then later below we
1702+ # see that that behavior remains even after optimizations.
1703+ output = run_d8_wasm (wasm , args = [second_wasm ])
1704+
1705+ if output == IGNORE :
1706+ # There is no point to continue since we can't compare this output
1707+ # to anything.
1708+ return
1709+
1710+ if output .strip () == 'exception thrown: failed to instantiate module' :
1711+ # We may fail to instantiate the modules for valid reasons, such as
1712+ # an active segment being out of bounds. There is no point to
1713+ # continue in such cases, as no exports are called.
1714+ return
1715+
1716+ # Make sure that fuzz_shell.js actually executed all exports from both
1717+ # wasm files.
1718+ exports = get_exports (wasm , ['func' ]) + get_exports (second_wasm , ['func' ])
1719+ assert output .count (FUZZ_EXEC_CALL_PREFIX ) == len (exports )
1720+
1721+ output = fix_output (output )
1722+
1723+ # Optimize at least one of the two.
1724+ wasms = [wasm , second_wasm ]
1725+ for i in range (random .randint (1 , 2 )):
1726+ wasm_index = random .randint (0 , 1 )
1727+ name = wasms [wasm_index ]
1728+ new_name = name + f'.opt{ i } .wasm'
1729+ opts = get_random_opts ()
1730+ run ([in_bin ('wasm-opt' ), name , '-o' , new_name ] + opts + FEATURE_OPTS )
1731+ wasms [wasm_index ] = new_name
1732+
1733+ # Run again, and compare the output
1734+ optimized_output = run_d8_wasm (wasms [0 ], args = [wasms [1 ]])
1735+ optimized_output = fix_output (optimized_output )
1736+
1737+ compare (output , optimized_output , 'Two' )
1738+
1739+ def can_run_on_wasm (self , wasm ):
1740+ # We cannot optimize wasm files we are going to link in closed world
1741+ # mode. We also cannot run shared-everything code in d8 yet. We also
1742+ # cannot compare if there are NaNs (as optimizations can lead to
1743+ # different outputs).
1744+ return not CLOSED_WORLD and all_disallowed (['shared-everything' ]) and not NANS
1745+
1746+
16731747# The global list of all test case handlers
16741748testcase_handlers = [
16751749 FuzzExec (),
@@ -1683,6 +1757,7 @@ def ensure(self):
16831757 # Split(),
16841758 RoundtripText (),
16851759 ClusterFuzz (),
1760+ Two (),
16861761]
16871762
16881763
@@ -2136,6 +2211,9 @@ def get_random_opts():
21362211# bash %(reduce_sh)s
21372212#
21382213# You may also need to add --timeout 5 or such if the testcase is a slow one.
2214+ #
2215+ # If the testcase handler uses a second wasm file, you may be able to reduce it
2216+ # using BINARYEN_SECOND_WASM.
21392217#
21402218 ''' % {'wasm_opt' : in_bin ('wasm-opt' ),
21412219 'bin' : shared .options .binaryen_bin ,
0 commit comments