@@ -1783,9 +1783,9 @@ def ensure(self):
17831783 tar .close ()
17841784
17851785
1786- # Tests linking two wasm files at runtime, and that optimizations do not break
1787- # anything. This is similar to Split(), but rather than split a wasm file into
1788- # two and link them at runtime, this starts with two separate wasm files .
1786+ # Generates two wasm and tests interesting interactions between them. This is a
1787+ # little similar to Split(), but rather than split one wasm file into two and
1788+ # test that, we start with two.
17891789#
17901790# Fuzzing failures here is a little trickier, as there are two wasm files.
17911791# You can reduce the primary file by finding the secondary one in the log
@@ -1812,7 +1812,7 @@ def ensure(self):
18121812class Two (TestCaseHandler ):
18131813 # Run at relatively high priority, as this is the main place we check cross-
18141814 # module interactions.
1815- frequency = 1
1815+ frequency = 1 # TODO: We may want even higher priority here
18161816
18171817 def handle (self , wasm ):
18181818 # Generate a second wasm file. (For fuzzing, we may be given one, but we
@@ -1865,9 +1865,50 @@ def handle(self, wasm):
18651865 print (f'warning: no calls in output. output:\n { output } ' )
18661866 assert calls_in_output == len (exports ), exports
18671867
1868+ # Merge the files and run them that way. The result should be the same,
1869+ # even if we optimize. TODO: merge (no pun intended) the rest of Merge
1870+ # into here.
1871+ merged = abspath ('merged.wasm' )
1872+ run ([in_bin ('wasm-merge' ), wasm , 'primary' , second_wasm , 'secondary' ,
1873+ '-o' , merged , '--rename-export-conflicts' , '-all' ])
1874+
1875+ # Usually also optimize the merged module. Optimizations are very
1876+ # interesting here, because after merging we can safely do even closed-
1877+ # world optimizations, making very aggressive changes that should still
1878+ # behave the same as before merging.
1879+ if random .random () < 0.8 :
1880+ merged_opt = abspath ('merged.opt.wasm' )
1881+ opts = get_random_opts ()
1882+ run ([in_bin ('wasm-opt' ), merged , '-o' , merged_opt , '-all' ] + opts )
1883+ merged = merged_opt
1884+
1885+ if not wasm_notices_export_changes (merged ):
1886+ # wasm-merge combines exports, which can alter their indexes and
1887+ # lead to noticeable differences if the wasm is sensitive to such
1888+ # things. We only compare the output if that is not an issue.
1889+ merged_output = run_bynterp (merged , args = ['--fuzz-exec-before' , '-all' ])
1890+
1891+ if merged_output == IGNORE :
1892+ # The original output was ok, but after merging it becomes
1893+ # something we must ignore. This can happen when we optimize, if
1894+ # the optimizer reorders a normal trap (say a null exception)
1895+ # with a host limit trap (say an allocation limit). Nothing to
1896+ # do here, but verify we did optimize, as otherwise this is
1897+ # inexplicable.
1898+ assert merged == abspath ('merged.opt.wasm' )
1899+ else :
1900+ self .compare_to_merged_output (output , merged_output )
1901+
1902+ # The rest of the testing here depends on being to optimize the
1903+ # two modules independently, which closed-world can break.
1904+ if CLOSED_WORLD :
1905+ return
1906+
1907+ # Fix up the normal output for later comparisons.
18681908 output = fix_output (output )
18691909
1870- # Optimize at least one of the two.
1910+ # We can optimize and compare the results. Optimize at least one of
1911+ # the two.
18711912 wasms = [wasm , second_wasm ]
18721913 for i in range (random .randint (1 , 2 )):
18731914 wasm_index = random .randint (0 , 1 )
@@ -1881,7 +1922,7 @@ def handle(self, wasm):
18811922 optimized_output = run_bynterp (wasms [0 ], args = ['--fuzz-exec-before' , f'--fuzz-exec-second={ wasms [1 ]} ' ])
18821923 optimized_output = fix_output (optimized_output )
18831924
1884- compare (output , optimized_output , 'Two' )
1925+ compare (output , optimized_output , 'Two-Opt ' )
18851926
18861927 # If we can, also test in V8. We also cannot compare if there are NaNs
18871928 # (as optimizations can lead to different outputs), and we must
@@ -1907,10 +1948,56 @@ def handle(self, wasm):
19071948
19081949 compare (output , optimized_output , 'Two-V8' )
19091950
1910- def can_run_on_wasm (self , wasm ):
1911- # We cannot optimize wasm files we are going to link in closed world
1912- # mode.
1913- return not CLOSED_WORLD
1951+ def compare_to_merged_output (self , output , merged_output ):
1952+ # Comparing the original output from two files to the output after
1953+ # merging them is not trivial. First, remove the extra logging that
1954+ # --fuzz-exec-second adds.
1955+ output = output .replace ('[fuzz-exec] running second module\n ' , '' )
1956+
1957+ # Fix up both outputs.
1958+ output = fix_output (output )
1959+ merged_output = fix_output (merged_output )
1960+
1961+ # Finally, align the export names. We merged with
1962+ # --rename-export-conflicts, so that all exports remain exported,
1963+ # allowing a full comparison, but we do need to handle the different
1964+ # names. We do so by matching the export names in the logging.
1965+ output_lines = output .splitlines ()
1966+ merged_output_lines = merged_output .splitlines ()
1967+
1968+ if len (output_lines ) != len (merged_output_lines ):
1969+ # The line counts don't even match. Just compare them, which will
1970+ # emit a nice error for that.
1971+ compare (output , merged_output , 'Two-Counts' )
1972+ assert False , 'we should have errored on the line counts'
1973+
1974+ for i in range (len (output_lines )):
1975+ a = output_lines [i ]
1976+ b = merged_output_lines [i ]
1977+ if a == b :
1978+ continue
1979+ if a .startswith (FUZZ_EXEC_CALL_PREFIX ):
1980+ # Fix up
1981+ # [fuzz-exec] calling foo/bar
1982+ # for different foo/bar. Just copy the original.
1983+ assert b .startswith (FUZZ_EXEC_CALL_PREFIX )
1984+ merged_output_lines [i ] = output_lines [i ]
1985+ elif a .startswith (FUZZ_EXEC_NOTE_RESULT ):
1986+ # Fix up
1987+ # [fuzz-exec] note result: foo/bar => 42
1988+ # for different foo/bar. We do not want to copy the result here,
1989+ # which might differ (that would be a bug we want to find).
1990+ assert b .startswith (FUZZ_EXEC_NOTE_RESULT )
1991+ assert a .count (' => ' ) == 1
1992+ assert b .count (' => ' ) == 1
1993+ a_prefix , a_result = a .split (' => ' )
1994+ b_prefix , b_result = b .split (' => ' )
1995+ # Copy a's prefix with b's result.
1996+ merged_output_lines [i ] = a_prefix + ' => ' + b_result
1997+
1998+ merged_output = '\n ' .join (merged_output_lines )
1999+
2000+ compare (output , merged_output , 'Two-Merged' )
19142001
19152002
19162003# Test --fuzz-preserve-imports-exports, which never modifies imports or exports.
0 commit comments