@@ -743,8 +743,8 @@ def run_d8_js(js, args=[], liftoff=True):
743
743
FUZZ_SHELL_JS = in_binaryen ('scripts' , 'fuzz_shell.js' )
744
744
745
745
746
- def run_d8_wasm (wasm , liftoff = True ):
747
- return run_d8_js (FUZZ_SHELL_JS , [wasm ], liftoff = liftoff )
746
+ def run_d8_wasm (wasm , liftoff = True , args = [] ):
747
+ return run_d8_js (FUZZ_SHELL_JS , [wasm ] + args , liftoff = liftoff )
748
748
749
749
750
750
def all_disallowed (features ):
@@ -1391,6 +1391,111 @@ def handle(self, wasm):
1391
1391
compare_between_vms (output , merged_output , 'Merge' )
1392
1392
1393
1393
1394
+ FUNC_NAMES_REGEX = re .compile (r'\n [(]func [$](\S+)' )
1395
+
1396
+
1397
+ # Tests wasm-split
1398
+ class Split (TestCaseHandler ):
1399
+ frequency = 1 # TODO: adjust lower when we actually enable this
1400
+
1401
+ def handle (self , wasm ):
1402
+ # get the list of function names, some of which we will decide to split
1403
+ # out
1404
+ wat = run ([in_bin ('wasm-dis' ), wasm ] + FEATURE_OPTS )
1405
+ all_funcs = re .findall (FUNC_NAMES_REGEX , wat )
1406
+
1407
+ # get the original output before splitting
1408
+ output = run_d8_wasm (wasm )
1409
+ output = fix_output (output )
1410
+
1411
+ # find the names of the exports. we need this because when we split the
1412
+ # module then new exports appear to connect the two halves of the
1413
+ # original module. we do not want to call all the exports on the new
1414
+ # primary module, but only the original ones.
1415
+ exports = []
1416
+ for line in output .splitlines ():
1417
+ if FUZZ_EXEC_CALL_PREFIX in line :
1418
+ exports .append (get_export_from_call_line (line ))
1419
+
1420
+ # pick which to split out, with a random rate of picking (biased towards
1421
+ # 0.5).
1422
+ rate = (random .random () + random .random ()) / 2
1423
+ split_funcs = []
1424
+ for func in all_funcs :
1425
+ if random .random () < rate :
1426
+ split_funcs .append (func )
1427
+
1428
+ if not split_funcs :
1429
+ # nothing to split out
1430
+ return
1431
+
1432
+ # split the wasm into two
1433
+ primary = wasm + '.primary.wasm'
1434
+ secondary = wasm + '.secondary.wasm'
1435
+
1436
+ # we require reference types, because that allows us to create our own
1437
+ # table. without that we use the existing table, and that may interact
1438
+ # with user code in odd ways (it really only works with the particular
1439
+ # form of table+segments that LLVM emits, and not with random fuzzer
1440
+ # content).
1441
+ split_feature_opts = FEATURE_OPTS + ['--enable-reference-types' ]
1442
+
1443
+ run ([in_bin ('wasm-split' ), wasm , '--split' ,
1444
+ '--split-funcs' , ',' .join (split_funcs ),
1445
+ '--primary-output' , primary ,
1446
+ '--secondary-output' , secondary ] + split_feature_opts )
1447
+
1448
+ # sometimes also optimize the split modules
1449
+ optimized = False
1450
+
1451
+ def optimize (name ):
1452
+ # do not optimize if it would change the ABI
1453
+ if CLOSED_WORLD :
1454
+ return name
1455
+ # TODO: use other optimizations here, but we'd need to be careful of
1456
+ # anything that can alter the ABI, and also current
1457
+ # limitations of open-world optimizations (see discussion in
1458
+ # https://github.com/WebAssembly/binaryen/pull/6660)
1459
+ opts = ['-O3' ]
1460
+ new_name = name + '.opt.wasm'
1461
+ run ([in_bin ('wasm-opt' ), name , '-o' , new_name , '-all' ] + opts + split_feature_opts )
1462
+ nonlocal optimized
1463
+ optimized = True
1464
+ return new_name
1465
+
1466
+ if random .random () < 0.5 :
1467
+ primary = optimize (primary )
1468
+ if random .random () < 0.5 :
1469
+ secondary = optimize (secondary )
1470
+
1471
+ # prepare the list of exports to call. the format is
1472
+ #
1473
+ # exports:A,B,C
1474
+ #
1475
+ exports_to_call = 'exports:' + ',' .join (exports )
1476
+
1477
+ # get the output from the split modules, linking them using JS
1478
+ # TODO run liftoff/turboshaft/etc.
1479
+ linked_output = run_d8_wasm (primary , args = [secondary , exports_to_call ])
1480
+ linked_output = fix_output (linked_output )
1481
+
1482
+ # see D8.can_compare_to_self: we cannot compare optimized outputs if
1483
+ # NaNs are allowed, as the optimizer can modify NaNs differently than
1484
+ # the JS engine.
1485
+ if not (NANS and optimized ):
1486
+ compare_between_vms (output , linked_output , 'Split' )
1487
+
1488
+ def can_run_on_feature_opts (self , feature_opts ):
1489
+ # to run the split wasm we use JS, that is, JS links the exports of one
1490
+ # to the imports of the other, etc. since we run in JS, the wasm must be
1491
+ # valid for JS.
1492
+ if not LEGALIZE :
1493
+ return False
1494
+
1495
+ # see D8.can_run
1496
+ return all_disallowed (['shared-everything' ])
1497
+
1498
+
1394
1499
# Check that the text format round-trips without error.
1395
1500
class RoundtripText (TestCaseHandler ):
1396
1501
frequency = 0.05
@@ -1413,6 +1518,8 @@ def handle(self, wasm):
1413
1518
TrapsNeverHappen (),
1414
1519
CtorEval (),
1415
1520
Merge (),
1521
+ # TODO: enable when stable enough, and adjust |frequency| (see above)
1522
+ # Split(),
1416
1523
RoundtripText ()
1417
1524
]
1418
1525
0 commit comments