summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/fuzz_opt.py5
-rw-r--r--scripts/test/shared.py7
-rwxr-xr-xscripts/update_lit_checks.py179
3 files changed, 186 insertions, 5 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py
index 7e6753700..d80829a10 100755
--- a/scripts/fuzz_opt.py
+++ b/scripts/fuzz_opt.py
@@ -151,7 +151,7 @@ def randomize_fuzz_settings():
IMPORTANT_INITIAL_CONTENTS = [
- os.path.join('passes', 'optimize-instructions_all-features.wast'),
+ os.path.join('lit', 'passes', 'optimize-instructions.wast'),
os.path.join('passes', 'optimize-instructions_fuzz-exec.wast'),
]
IMPORTANT_INITIAL_CONTENTS = [os.path.join(shared.get_test_dir('.'), t) for t in IMPORTANT_INITIAL_CONTENTS]
@@ -833,7 +833,8 @@ spec_tests = shared.get_tests(shared.get_test_dir('spec'), test_suffixes)
wasm2js_tests = shared.get_tests(shared.get_test_dir('wasm2js'), test_suffixes)
lld_tests = shared.get_tests(shared.get_test_dir('lld'), test_suffixes)
unit_tests = shared.get_tests(shared.get_test_dir(os.path.join('unit', 'input')), test_suffixes)
-all_tests = core_tests + passes_tests + spec_tests + wasm2js_tests + lld_tests + unit_tests
+lit_tests = shared.get_tests(shared.get_test_dir('lit'), test_suffixes, recursive=True)
+all_tests = core_tests + passes_tests + spec_tests + wasm2js_tests + lld_tests + unit_tests + lit_tests
# Do one test, given an input file for -ttf and some optimizations to run
diff --git a/scripts/test/shared.py b/scripts/test/shared.py
index 174967b42..f782bb849 100644
--- a/scripts/test/shared.py
+++ b/scripts/test/shared.py
@@ -366,15 +366,16 @@ def get_test_dir(name):
return os.path.join(options.binaryen_test, name)
-def get_tests(test_dir, extensions=[]):
+def get_tests(test_dir, extensions=[], recursive=False):
"""Returns the list of test files in a given directory. 'extensions' is a
list of file extensions. If 'extensions' is empty, returns all files.
"""
tests = []
+ star = '**/*' if recursive else '*'
if not extensions:
- tests += glob.glob(os.path.join(test_dir, '*'))
+ tests += glob.glob(os.path.join(test_dir, star), recursive=True)
for ext in extensions:
- tests += glob.glob(os.path.join(test_dir, '*' + ext))
+ tests += glob.glob(os.path.join(test_dir, star + ext), recursive=True)
if options.test_name_filter:
tests = fnmatch.filter(tests, options.test_name_filter)
return sorted(tests)
diff --git a/scripts/update_lit_checks.py b/scripts/update_lit_checks.py
new file mode 100755
index 000000000..d8446288a
--- /dev/null
+++ b/scripts/update_lit_checks.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+# Copyright 2021 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.
+
+"""A test case update script.
+
+This script is a utility to update wasm-opt based lit tests with new FileCheck
+patterns. It is based on LLVM's update_llc_test_checks.py script.
+"""
+
+import argparse
+import glob
+import os
+import re
+import subprocess
+import sys
+import tempfile
+
+
+script_name = os.path.basename(__file__)
+NOTICE = (f';; NOTE: Assertions have been generated by {script_name} and ' +
+ 'should not be edited.')
+RUN_LINE_RE = re.compile(r'^\s*;;\s*RUN:\s*(.*)$')
+CHECK_PREFIX_RE = re.compile(r'.*--check-prefix[= ](\S+).*')
+FUNC_RE = re.compile(r'(^\s*)\(func \$(\S*).*$', re.MULTILINE)
+
+
+def warn(msg):
+ print(f'WARNING: {msg}', file=sys.stderr)
+
+
+def itertests(args):
+ """
+ Yield (filename, lines) for each test specified in the command line args
+ """
+ for pattern in args.tests:
+ tests = glob.glob(pattern, recursive=True)
+ if not tests:
+ warn(f'No tests matched {pattern}. Ignoring it.')
+ continue
+ for test in tests:
+ with open(test) as f:
+ lines = [line.rstrip() for line in f]
+ first_line = lines[0] if lines else ''
+ if script_name not in first_line and not args.force:
+ warn(f'Skipping test {test} which was not generated by '
+ f'{script_name}. Use -f to override.')
+ continue
+ yield test, lines
+
+
+def find_run_lines(test, lines):
+ line_matches = [RUN_LINE_RE.match(l) for l in lines]
+ matches = [match.group(1) for match in line_matches if match]
+ if not matches:
+ warn(f'No RUN lines found in {test}. Ignoring.')
+ return []
+ run_lines = [matches[0]]
+ for line in matches[1:]:
+ if run_lines[-1].endswith('\\'):
+ run_lines[-1] = run_lines[-1].rstrip('\\') + ' ' + line
+ else:
+ run_lines.append(line)
+ return run_lines
+
+
+def run_command(args, test, tmp, command):
+ env = dict(os.environ)
+ env['PATH'] = args.binaryen_bin + os.pathsep + env['PATH']
+ command = command.replace('%s', test)
+ command = command.replace('%t', tmp)
+ return subprocess.check_output(command, shell=True, env=env).decode('utf-8')
+
+
+def find_funcs(module):
+ """Return a dict mapping each function name to lines in the function"""
+ result = {}
+ for match in FUNC_RE.finditer(module):
+ name = match.group(2)
+ depth = 1
+ for end in range(match.end(), len(module)):
+ if depth == 0:
+ break
+ elif module[end] == '(':
+ depth += 1
+ elif module[end] == ')':
+ depth -= 1
+ result[name] = module[match.start():end].split('\n')
+ return result
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ '--binaryen-bin', dest='binaryen_bin', default='bin',
+ help=('Specifies the path to the Binaryen executables in the CMake build'
+ ' directory. Default: bin/ of current directory (i.e. assume an'
+ ' in-tree build).'))
+ parser.add_argument(
+ '-f', '--force', action='store_true',
+ help=('Generate FileCheck patterns even for test files whose existing '
+ 'patterns were not generated by this script.'))
+ parser.add_argument(
+ '--dry-run', action='store_true',
+ help=('Print the updated test file contents instead of changing the '
+ 'test files'))
+ parser.add_argument('tests', nargs='+', help='The test files to update')
+ args = parser.parse_args()
+ args.binaryen_bin = os.path.abspath(args.binaryen_bin)
+
+ tmp = tempfile.mktemp()
+
+ for test, lines in itertests(args):
+ run_list = []
+ for line in find_run_lines(test, lines):
+ commands = [cmd.strip() for cmd in line.rsplit('|', 1)]
+ filecheck_cmd = ''
+ if len(commands) > 1 and commands[1].startswith('filecheck '):
+ filecheck_cmd = commands[1]
+ commands = commands[:1]
+
+ check_prefix = ''
+ if filecheck_cmd.startswith('filecheck '):
+ prefix_match = CHECK_PREFIX_RE.match(filecheck_cmd)
+ if prefix_match:
+ check_prefix = prefix_match.group(1)
+ else:
+ check_prefix = 'CHECK'
+
+ run_list.append((check_prefix, commands[0]))
+
+ # Map check prefixes and function names to the corresponding output
+ func_dict = {}
+ for prefix, command, in run_list:
+ output = run_command(args, test, tmp, command)
+ if prefix:
+ func_dict[prefix] = find_funcs(output)
+
+ check_line_re = re.compile(r'^\s*;;\s*(' + '|'.join(func_dict.keys()) +
+ r')(?:-NEXT|-LABEL|-NOT)?: .*$')
+ output_lines = [NOTICE]
+ if lines and script_name in lines[0]:
+ lines = lines[1:]
+ for line in lines:
+ if check_line_re.match(line):
+ continue
+ func_match = FUNC_RE.match(line)
+ if func_match:
+ indent, name = func_match.groups()
+ for prefix, funcs in func_dict.items():
+ body = funcs.get(name, [])
+ if not body:
+ continue
+ output_lines.append(f'{indent};; {prefix}: {body[0]}')
+ for l in body[1:]:
+ output_lines.append(f'{indent};; {prefix}-NEXT:{l}')
+ output_lines.append(line)
+
+ if args.dry_run:
+ print('\n'.join(output_lines))
+ else:
+ with open(test, 'w') as f:
+ for line in output_lines:
+ f.write(line + '\n')
+
+
+if __name__ == '__main__':
+ main()