@@ -1360,7 +1360,7 @@ def can_run_on_wasm(self, wasm):
1360
1360
1361
1361
# Tests wasm-ctor-eval
1362
1362
class CtorEval (TestCaseHandler ):
1363
- frequency = 0.2
1363
+ frequency = 0.1
1364
1364
1365
1365
def handle (self , wasm ):
1366
1366
# get the expected execution results.
@@ -1477,7 +1477,9 @@ def can_run_on_wasm(self, wasm):
1477
1477
FUNC_NAMES_REGEX = re .compile (r'\n [(]func [$](\S+)' )
1478
1478
1479
1479
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).
1481
1483
class Split (TestCaseHandler ):
1482
1484
frequency = 1 # TODO: adjust lower when we actually enable this
1483
1485
@@ -1670,6 +1672,78 @@ def ensure(self):
1670
1672
tar .close ()
1671
1673
1672
1674
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
+
1673
1747
# The global list of all test case handlers
1674
1748
testcase_handlers = [
1675
1749
FuzzExec (),
@@ -1683,6 +1757,7 @@ def ensure(self):
1683
1757
# Split(),
1684
1758
RoundtripText (),
1685
1759
ClusterFuzz (),
1760
+ Two (),
1686
1761
]
1687
1762
1688
1763
@@ -2136,6 +2211,9 @@ def get_random_opts():
2136
2211
# bash %(reduce_sh)s
2137
2212
#
2138
2213
# 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.
2139
2217
#
2140
2218
''' % {'wasm_opt' : in_bin ('wasm-opt' ),
2141
2219
'bin' : shared .options .binaryen_bin ,
0 commit comments