blob: 522c1be05f08f1dda639c908e2137a6e78961b62 [file] [log] [blame]
#! /usr/bin/env python
# Copyright 2016 WebAssembly Community Group participants
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''
This fuzzes passes, by starting with a wast, then running
random passes on the wast, and seeing if they break optimization
or validation
Usage: Provide the filename of the wast.
'''
import os
import random
import shutil
import subprocess
import sys
PASSES = [
"duplicate-function-elimination",
"dce",
"remove-unused-brs",
"remove-unused-names",
"optimize-instructions",
"precompute",
"simplify-locals",
"vacuum",
"coalesce-locals",
"reorder-locals",
"merge-blocks",
"remove-unused-functions",
]
# main
wast = sys.argv[1]
print '>>> wast:', wast
args = sys.argv[2:]
def run():
try:
cmd = ['bin/wasm-opt', wast]
print 'run', cmd
subprocess.check_call(cmd, stderr=open('/dev/null'))
except Exception, e:
return ">>> !!! ", e, " !!!"
return 'ok'
original_wast = None
try:
# get normal output
normal = run()
print '>>> normal output:\n', normal
assert normal, 'must be output'
# ensure we actually use the wast
original_wast = wast + '.original.wast'
shutil.move(wast, original_wast)
def apply_passes(passes):
wasm_opt = os.path.join('bin', 'wasm-opt')
subprocess.check_call([wasm_opt, original_wast] + passes + ['-o', wast],
stderr=open('/dev/null'))
# loop, looking for failures
def simplify(passes):
# passes is known to fail, try to simplify down by removing
more = True
while more:
more = False
print '>>> trying to reduce:', ' '.join(passes),
print ' [' + str(len(passes)) + ']'
for i in range(len(passes)):
smaller = passes[:i] + passes[i + 1:]
print '>>>>>> try to reduce to:', ' '.join(smaller),
print ' [' + str(len(smaller)) + ']'
try:
apply_passes(smaller)
assert run() == normal
except:
# this failed too, so it's a good reduction
passes = smaller
print '>>> reduction successful'
more = True
break
print '>>> reduced to:', ' '.join(passes)
tested = set()
def pick_passes():
# return '--waka'.split(' ')
ret = []
while 1:
str_ret = str(ret)
if random.random() < 0.5 and str_ret not in tested:
tested.add(str_ret)
return ret
ret.append('--' + random.choice(PASSES))
counter = 0
while 1:
passes = pick_passes()
print '>>> [' + str(counter) + '] testing:', ' '.join(passes)
counter += 1
try:
apply_passes(passes)
except Exception, e:
print e
simplify(passes)
break
seen = run()
if seen != normal:
print '>>> bad output:\n', seen
simplify(passes)
break
finally:
if original_wast:
shutil.move(original_wast, wast)