@@ -786,179 +786,187 @@ class FuzzExec(TestCaseHandler):
786786 def handle_pair (self , input , before_wasm , after_wasm , opts ):
787787 run ([in_bin ('wasm-opt' ), before_wasm ] + opts + ['--fuzz-exec' ])
788788
789+ # VMs
790+
791+
792+ class BinaryenInterpreter :
793+ name = 'binaryen interpreter'
794+
795+ def run (self , wasm ):
796+ output = run_bynterp (wasm , ['--fuzz-exec-before' ])
797+ if output != IGNORE :
798+ calls = output .count (FUZZ_EXEC_EXPORT_PREFIX )
799+ errors = output .count (TRAP_PREFIX ) + output .count (HOST_LIMIT_PREFIX )
800+ if errors > calls / 2 :
801+ # A significant amount of execution on this testcase
802+ # simply trapped, and was not very useful, so mark it
803+ # as ignored. Ideally the fuzzer testcases would be
804+ # improved to reduce this number.
805+ #
806+ # Note that we don't change output=IGNORE as there may
807+ # still be useful testing here (up to 50%), so we only
808+ # note that this is a mostly-ignored run, but we do not
809+ # ignore the parts that are useful.
810+ #
811+ # Note that we set amount to 0.5 because we are run both
812+ # on the before wasm and the after wasm. Those will be
813+ # in sync (because the optimizer does not remove traps)
814+ # and so by setting 0.5 we only increment by 1 for the
815+ # entire iteration.
816+ note_ignored_vm_run ('too many errors vs calls' ,
817+ extra_text = f' ({ calls } calls, { errors } errors)' ,
818+ amount = 0.5 )
819+ return output
820+
821+ def can_run (self , wasm ):
822+ return True
823+
824+ def can_compare_to_self (self ):
825+ return True
826+
827+ def can_compare_to_other (self , other ):
828+ return True
829+
830+
831+ class D8 :
832+ name = 'd8'
833+
834+ def run (self , wasm , extra_d8_flags = []):
835+ return run_vm ([shared .V8 , get_fuzz_shell_js ()] + shared .V8_OPTS + get_v8_extra_flags () + extra_d8_flags + ['--' , wasm ])
836+
837+ def can_run (self , wasm ):
838+ return all_disallowed (DISALLOWED_FEATURES_IN_V8 )
839+
840+ def can_compare_to_self (self ):
841+ # With nans, VM differences can confuse us, so only very simple VMs
842+ # can compare to themselves after opts in that case.
843+ return not NANS
844+
845+ def can_compare_to_other (self , other ):
846+ # Relaxed SIMD allows different behavior between VMs, so only
847+ # allow comparisons to other d8 variants if it is enabled.
848+ if not all_disallowed (['relaxed-simd' ]) and not other .name .startswith ('d8' ):
849+ return False
850+
851+ # If not legalized, the JS will fail immediately, so no point to
852+ # compare to others.
853+ return self .can_compare_to_self () and LEGALIZE
854+
855+
856+ class D8Liftoff (D8 ):
857+ name = 'd8_liftoff'
858+
859+ def run (self , wasm ):
860+ return super ().run (wasm , extra_d8_flags = V8_LIFTOFF_ARGS )
861+
862+
863+ class D8Turboshaft (D8 ):
864+ name = 'd8_turboshaft'
865+
866+ def run (self , wasm ):
867+ flags = ['--no-liftoff' ]
868+ return super ().run (wasm , extra_d8_flags = flags )
869+
870+
871+ class Wasm2C :
872+ name = 'wasm2c'
873+
874+ def __init__ (self ):
875+ # look for wabt in the path. if it's not here, don't run wasm2c
876+ try :
877+ wabt_bin = shared .which ('wasm2c' )
878+ wabt_root = os .path .dirname (os .path .dirname (wabt_bin ))
879+ self .wasm2c_dir = os .path .join (wabt_root , 'wasm2c' )
880+ if not os .path .isdir (self .wasm2c_dir ):
881+ print ('wabt found, but not wasm2c support dir' )
882+ self .wasm2c_dir = None
883+ except Exception as e :
884+ print ('warning: no wabt found:' , e )
885+ self .wasm2c_dir = None
886+
887+ def can_run (self , wasm ):
888+ if self .wasm2c_dir is None :
889+ return False
890+ # if we legalize for JS, the ABI is not what C wants
891+ if LEGALIZE :
892+ return False
893+ # relatively slow, so run it less frequently
894+ if random .random () < 0.5 :
895+ return False
896+ # wasm2c doesn't support most features
897+ return all_disallowed (['exception-handling' , 'simd' , 'threads' , 'bulk-memory' , 'nontrapping-float-to-int' , 'tail-call' , 'sign-ext' , 'reference-types' , 'multivalue' , 'gc' , 'custom-descriptors' , 'relaxed-atomics' ])
898+
899+ def run (self , wasm ):
900+ run ([in_bin ('wasm-opt' ), wasm , '--emit-wasm2c-wrapper=main.c' ] + FEATURE_OPTS )
901+ run (['wasm2c' , wasm , '-o' , 'wasm.c' ])
902+ compile_cmd = ['clang' , 'main.c' , 'wasm.c' , os .path .join (self .wasm2c_dir , 'wasm-rt-impl.c' ), '-I' + self .wasm2c_dir , '-lm' , '-Werror' ]
903+ run (compile_cmd )
904+ return run_vm (['./a.out' ])
905+
906+ def can_compare_to_self (self ):
907+ # The binaryen optimizer changes NaNs in the ways that wasm
908+ # expects, but that's not quite what C has
909+ return not NANS
910+
911+ def can_compare_to_other (self , other ):
912+ # C won't trap on OOB, and NaNs can differ from wasm VMs
913+ return not OOB and not NANS
914+
915+
916+ class Wasm2C2Wasm (Wasm2C ):
917+ name = 'wasm2c2wasm'
918+
919+ def __init__ (self ):
920+ super ().__init__ ()
921+
922+ self .has_emcc = shared .which ('emcc' ) is not None
923+
924+ def run (self , wasm ):
925+ run ([in_bin ('wasm-opt' ), wasm , '--emit-wasm2c-wrapper=main.c' ] + FEATURE_OPTS )
926+ run (['wasm2c' , wasm , '-o' , 'wasm.c' ])
927+ compile_cmd = ['emcc' , 'main.c' , 'wasm.c' ,
928+ os .path .join (self .wasm2c_dir , 'wasm-rt-impl.c' ),
929+ '-I' + self .wasm2c_dir ,
930+ '-lm' ,
931+ '-s' , 'ENVIRONMENT=shell' ,
932+ '-s' , 'ALLOW_MEMORY_GROWTH' ]
933+ # disable the signal handler: emcc looks like unix, but wasm has
934+ # no signals
935+ compile_cmd += ['-DWASM_RT_MEMCHECK_SIGNAL_HANDLER=0' ]
936+ if random .random () < 0.5 :
937+ compile_cmd += ['-O' + str (random .randint (1 , 3 ))]
938+ elif random .random () < 0.5 :
939+ if random .random () < 0.5 :
940+ compile_cmd += ['-Os' ]
941+ else :
942+ compile_cmd += ['-Oz' ]
943+ # avoid pass-debug on the emcc invocation itself (which runs
944+ # binaryen to optimize the wasm), as the wasm here can be very
945+ # large and it isn't what we are focused on testing here
946+ with no_pass_debug ():
947+ run (compile_cmd )
948+ return run_d8_js (abspath ('a.out.js' ))
949+
950+ def can_run (self , wasm ):
951+ # quite slow (more steps), so run it less frequently
952+ if random .random () < 0.8 :
953+ return False
954+ # prefer not to run if the wasm is very large, as it can OOM
955+ # the JS engine.
956+ return super ().can_run (wasm ) and self .has_emcc and \
957+ os .path .getsize (wasm ) <= INPUT_SIZE_MEAN
958+
959+ def can_compare_to_other (self , other ):
960+ # NaNs can differ from wasm VMs
961+ return not NANS
962+
789963
790964class CompareVMs (TestCaseHandler ):
791965 frequency = 1
792966
793967 def __init__ (self ):
794968 super ().__init__ ()
795969
796- class BinaryenInterpreter :
797- name = 'binaryen interpreter'
798-
799- def run (self , wasm ):
800- output = run_bynterp (wasm , ['--fuzz-exec-before' ])
801- if output != IGNORE :
802- calls = output .count (FUZZ_EXEC_EXPORT_PREFIX )
803- errors = output .count (TRAP_PREFIX ) + output .count (HOST_LIMIT_PREFIX )
804- if errors > calls / 2 :
805- # A significant amount of execution on this testcase
806- # simply trapped, and was not very useful, so mark it
807- # as ignored. Ideally the fuzzer testcases would be
808- # improved to reduce this number.
809- #
810- # Note that we don't change output=IGNORE as there may
811- # still be useful testing here (up to 50%), so we only
812- # note that this is a mostly-ignored run, but we do not
813- # ignore the parts that are useful.
814- #
815- # Note that we set amount to 0.5 because we are run both
816- # on the before wasm and the after wasm. Those will be
817- # in sync (because the optimizer does not remove traps)
818- # and so by setting 0.5 we only increment by 1 for the
819- # entire iteration.
820- note_ignored_vm_run ('too many errors vs calls' ,
821- extra_text = f' ({ calls } calls, { errors } errors)' ,
822- amount = 0.5 )
823- return output
824-
825- def can_run (self , wasm ):
826- return True
827-
828- def can_compare_to_self (self ):
829- return True
830-
831- def can_compare_to_other (self , other ):
832- return True
833-
834- class D8 :
835- name = 'd8'
836-
837- def run (self , wasm , extra_d8_flags = []):
838- return run_vm ([shared .V8 , get_fuzz_shell_js ()] + shared .V8_OPTS + get_v8_extra_flags () + extra_d8_flags + ['--' , wasm ])
839-
840- def can_run (self , wasm ):
841- return all_disallowed (DISALLOWED_FEATURES_IN_V8 )
842-
843- def can_compare_to_self (self ):
844- # With nans, VM differences can confuse us, so only very simple VMs
845- # can compare to themselves after opts in that case.
846- return not NANS
847-
848- def can_compare_to_other (self , other ):
849- # Relaxed SIMD allows different behavior between VMs, so only
850- # allow comparisons to other d8 variants if it is enabled.
851- if not all_disallowed (['relaxed-simd' ]) and not other .name .startswith ('d8' ):
852- return False
853-
854- # If not legalized, the JS will fail immediately, so no point to
855- # compare to others.
856- return self .can_compare_to_self () and LEGALIZE
857-
858- class D8Liftoff (D8 ):
859- name = 'd8_liftoff'
860-
861- def run (self , wasm ):
862- return super ().run (wasm , extra_d8_flags = V8_LIFTOFF_ARGS )
863-
864- class D8Turboshaft (D8 ):
865- name = 'd8_turboshaft'
866-
867- def run (self , wasm ):
868- flags = ['--no-liftoff' ]
869- return super ().run (wasm , extra_d8_flags = flags )
870-
871- class Wasm2C :
872- name = 'wasm2c'
873-
874- def __init__ (self ):
875- # look for wabt in the path. if it's not here, don't run wasm2c
876- try :
877- wabt_bin = shared .which ('wasm2c' )
878- wabt_root = os .path .dirname (os .path .dirname (wabt_bin ))
879- self .wasm2c_dir = os .path .join (wabt_root , 'wasm2c' )
880- if not os .path .isdir (self .wasm2c_dir ):
881- print ('wabt found, but not wasm2c support dir' )
882- self .wasm2c_dir = None
883- except Exception as e :
884- print ('warning: no wabt found:' , e )
885- self .wasm2c_dir = None
886-
887- def can_run (self , wasm ):
888- if self .wasm2c_dir is None :
889- return False
890- # if we legalize for JS, the ABI is not what C wants
891- if LEGALIZE :
892- return False
893- # relatively slow, so run it less frequently
894- if random .random () < 0.5 :
895- return False
896- # wasm2c doesn't support most features
897- return all_disallowed (['exception-handling' , 'simd' , 'threads' , 'bulk-memory' , 'nontrapping-float-to-int' , 'tail-call' , 'sign-ext' , 'reference-types' , 'multivalue' , 'gc' , 'custom-descriptors' , 'relaxed-atomics' ])
898-
899- def run (self , wasm ):
900- run ([in_bin ('wasm-opt' ), wasm , '--emit-wasm2c-wrapper=main.c' ] + FEATURE_OPTS )
901- run (['wasm2c' , wasm , '-o' , 'wasm.c' ])
902- compile_cmd = ['clang' , 'main.c' , 'wasm.c' , os .path .join (self .wasm2c_dir , 'wasm-rt-impl.c' ), '-I' + self .wasm2c_dir , '-lm' , '-Werror' ]
903- run (compile_cmd )
904- return run_vm (['./a.out' ])
905-
906- def can_compare_to_self (self ):
907- # The binaryen optimizer changes NaNs in the ways that wasm
908- # expects, but that's not quite what C has
909- return not NANS
910-
911- def can_compare_to_other (self , other ):
912- # C won't trap on OOB, and NaNs can differ from wasm VMs
913- return not OOB and not NANS
914-
915- class Wasm2C2Wasm (Wasm2C ):
916- name = 'wasm2c2wasm'
917-
918- def __init__ (self ):
919- super ().__init__ ()
920-
921- self .has_emcc = shared .which ('emcc' ) is not None
922-
923- def run (self , wasm ):
924- run ([in_bin ('wasm-opt' ), wasm , '--emit-wasm2c-wrapper=main.c' ] + FEATURE_OPTS )
925- run (['wasm2c' , wasm , '-o' , 'wasm.c' ])
926- compile_cmd = ['emcc' , 'main.c' , 'wasm.c' ,
927- os .path .join (self .wasm2c_dir , 'wasm-rt-impl.c' ),
928- '-I' + self .wasm2c_dir ,
929- '-lm' ,
930- '-s' , 'ENVIRONMENT=shell' ,
931- '-s' , 'ALLOW_MEMORY_GROWTH' ]
932- # disable the signal handler: emcc looks like unix, but wasm has
933- # no signals
934- compile_cmd += ['-DWASM_RT_MEMCHECK_SIGNAL_HANDLER=0' ]
935- if random .random () < 0.5 :
936- compile_cmd += ['-O' + str (random .randint (1 , 3 ))]
937- elif random .random () < 0.5 :
938- if random .random () < 0.5 :
939- compile_cmd += ['-Os' ]
940- else :
941- compile_cmd += ['-Oz' ]
942- # avoid pass-debug on the emcc invocation itself (which runs
943- # binaryen to optimize the wasm), as the wasm here can be very
944- # large and it isn't what we are focused on testing here
945- with no_pass_debug ():
946- run (compile_cmd )
947- return run_d8_js (abspath ('a.out.js' ))
948-
949- def can_run (self , wasm ):
950- # quite slow (more steps), so run it less frequently
951- if random .random () < 0.8 :
952- return False
953- # prefer not to run if the wasm is very large, as it can OOM
954- # the JS engine.
955- return super ().can_run (wasm ) and self .has_emcc and \
956- os .path .getsize (wasm ) <= INPUT_SIZE_MEAN
957-
958- def can_compare_to_other (self , other ):
959- # NaNs can differ from wasm VMs
960- return not NANS
961-
962970 # the binaryen interpreter is specifically useful for various things
963971 self .bynterpreter = BinaryenInterpreter ()
964972
0 commit comments