summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2016-07-13 12:15:20 -0700
committerAlon Zakai <alonzakai@gmail.com>2016-07-13 16:07:59 -0700
commit9e2f21c9aa1993325b5a7043d206e2baf07e9180 (patch)
tree7fc213c0bde0d4da747d85ec791f08c7f083029a
parent9d3801039d2e2f9738d2c4829ee59aa047da1cc6 (diff)
downloadbinaryen-9e2f21c9aa1993325b5a7043d206e2baf07e9180.tar.gz
binaryen-9e2f21c9aa1993325b5a7043d206e2baf07e9180.tar.bz2
binaryen-9e2f21c9aa1993325b5a7043d206e2baf07e9180.zip
separate wasm-opt out from wasm-shell: opt optimizes, shell runs wast shell tests
-rw-r--r--CMakeLists.txt11
-rwxr-xr-xauto_update_tests.py12
-rwxr-xr-xcheck.py86
-rwxr-xr-xscripts/support.py57
-rw-r--r--src/tools/wasm-as.cpp2
-rw-r--r--src/tools/wasm-opt.cpp102
-rw-r--r--src/tools/wasm-shell.cpp45
m---------test/waterfall0
8 files changed, 200 insertions, 115 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 10120844e..38b2df763 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -120,6 +120,17 @@ SET_PROPERTY(TARGET wasm-shell PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET wasm-shell PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS wasm-shell DESTINATION bin)
+SET(wasm-opt_SOURCES
+ src/tools/wasm-opt.cpp
+ src/wasm.cpp
+)
+ADD_EXECUTABLE(wasm-opt
+ ${wasm-opt_SOURCES})
+TARGET_LINK_LIBRARIES(wasm-opt asmjs emscripten-optimizer ${all_passes} support)
+SET_PROPERTY(TARGET wasm-opt PROPERTY CXX_STANDARD 11)
+SET_PROPERTY(TARGET wasm-opt PROPERTY CXX_STANDARD_REQUIRED ON)
+INSTALL(TARGETS wasm-opt DESTINATION bin)
+
SET(asm2wasm_SOURCES
src/tools/asm2wasm.cpp
src/wasm.cpp
diff --git a/auto_update_tests.py b/auto_update_tests.py
index 9505c2dc4..3b6c9bf24 100755
--- a/auto_update_tests.py
+++ b/auto_update_tests.py
@@ -2,6 +2,8 @@
import os, sys, subprocess, difflib
+from scripts.support import run_command, split_wast
+
print '[ processing and updating testcases... ]\n'
for asm in sorted(os.listdir('test')):
@@ -68,9 +70,13 @@ for t in sorted(os.listdir(os.path.join('test', 'passes'))):
print '..', t
passname = os.path.basename(t).replace('.wast', '')
opts = ['-O'] if passname == 'O' else ['--' + p for p in passname.split('_')]
- cmd = [os.path.join('bin', 'wasm-shell')] + opts + [os.path.join('test', 'passes', t), '--print']
- print ' ', ' '.join(cmd)
- actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+ t = os.path.join('test', 'passes', t)
+ actual = ''
+ for module, asserts in split_wast(t):
+ assert len(asserts) == 0
+ open('split.wast', 'w').write(module)
+ cmd = [os.path.join('bin', 'wasm-opt')] + opts + ['split.wast', '--print']
+ actual += run_command(cmd)
open(os.path.join('test', 'passes', passname + '.txt'), 'w').write(actual)
print '\n[ checking binary format testcases... ]\n'
diff --git a/check.py b/check.py
index 4e6cd5e52..fb86721ea 100755
--- a/check.py
+++ b/check.py
@@ -17,7 +17,7 @@
import os, shutil, sys, subprocess, difflib, json, time, urllib2
import scripts.storage
-import scripts.support
+from scripts.support import run_command, split_wast
interpreter = None
requested = []
@@ -155,13 +155,6 @@ def delete_from_orbit(filename): # removes a file if it exists, using any and al
except:
pass
-def run_command(cmd, expected_status=0, stderr=None):
- print 'executing: ', ' '.join(cmd)
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr)
- out, err = proc.communicate()
- if proc.returncode != expected_status: raise Exception(('run_command failed', err))
- return out
-
def fail(actual, expected):
raise Exception("incorrect output, diff:\n\n%s" % (
''.join([a.rstrip()+'\n' for a in difflib.unified_diff(expected.split('\n'), actual.split('\n'), fromfile='expected', tofile='actual')])[:]
@@ -193,50 +186,6 @@ if not has_vanilla_emcc:
# check utilities
-def split_wast(wast):
- # .wast files can contain multiple modules, and assertions for each one.
- # this splits out a wast into [(module, assertions), ..]
- # we ignore module invalidity tests here.
- wast = open(wast).read()
- ret = []
- def to_end(j):
- depth = 1
- while depth > 0 and j < len(wast):
- if wast[j] == '"':
- j = wast.find('"', j + 1)
- elif wast[j] == '(':
- depth += 1
- elif wast[j] == ')':
- depth -= 1
- elif wast[j] == ';' and wast[j+1] == ';':
- j = wast.find('\n', j)
- j += 1
- return j
- i = 0
- while i >= 0:
- start = wast.find('(', i)
- if start >= 0 and wast[start+1] == ';':
- # block comment
- i = wast.find(';)', start+2)
- assert i > 0, wast[start:]
- i += 2
- continue
- skip = wast.find(';', i)
- if skip >= 0 and skip < start and skip + 1 < len(wast):
- if wast[skip+1] == ';':
- i = wast.find('\n', i) + 1
- continue
- if start < 0: break
- i = to_end(start + 1)
- chunk = wast[start:i]
- if chunk.startswith('(module'):
- ret += [(chunk, [])]
- elif chunk.startswith('(assert_invalid'):
- continue
- elif chunk.startswith(('(assert', '(invoke')):
- ret[-1][1].append(chunk)
- return ret
-
def binary_format_check(wast, verify_final_result=True):
# checks we can convert the wast to binary and back
@@ -270,11 +219,11 @@ def minify_check(wast, verify_final_result=True):
# checks we can parse minified output
print ' (minify check)'
- cmd = [os.path.join('bin', 'wasm-shell'), wast, '--print-minified']
+ cmd = [os.path.join('bin', 'wasm-opt'), wast, '--print-minified']
print ' ', ' '.join(cmd)
- subprocess.check_call([os.path.join('bin', 'wasm-shell'), wast, '--print-minified'], stdout=open('a.wasm', 'w'), stderr=subprocess.PIPE)
+ subprocess.check_call([os.path.join('bin', 'wasm-opt'), wast, '--print-minified'], stdout=open('a.wasm', 'w'), stderr=subprocess.PIPE)
assert os.path.exists('a.wasm')
- subprocess.check_call([os.path.join('bin', 'wasm-shell'), 'a.wasm', '--print-minified'], stdout=open('b.wasm', 'w'), stderr=subprocess.PIPE)
+ subprocess.check_call([os.path.join('bin', 'wasm-opt'), 'a.wasm', '--print-minified'], stdout=open('b.wasm', 'w'), stderr=subprocess.PIPE)
assert os.path.exists('b.wasm')
if verify_final_result:
expected = open('a.wasm').read()
@@ -301,23 +250,28 @@ for e in executables:
assert e in err, 'Expected help to contain program name, got:\n%s' % err
assert len(err.split('\n')) > 8, 'Expected some help, got:\n%s' % err
-print '\n[ checking wasm-shell -o notation... ]\n'
+print '\n[ checking wasm-opt -o notation... ]\n'
wast = os.path.join('test', 'hello_world.wast')
delete_from_orbit('a.wast')
-cmd = [os.path.join('bin', 'wasm-shell'), wast, '-o', 'a.wast']
+cmd = [os.path.join('bin', 'wasm-opt'), wast, '-o', 'a.wast']
run_command(cmd)
fail_if_not_identical(open('a.wast').read(), open(wast).read())
-print '\n[ checking wasm-shell passes... ]\n'
+print '\n[ checking wasm-opt passes... ]\n'
for t in sorted(os.listdir(os.path.join('test', 'passes'))):
if t.endswith('.wast'):
print '..', t
passname = os.path.basename(t).replace('.wast', '')
opts = ['-O'] if passname == 'O' else ['--' + p for p in passname.split('_')]
- cmd = [os.path.join('bin', 'wasm-shell')] + opts + [os.path.join('test', 'passes', t), '--print']
- actual = run_command(cmd)
+ t = os.path.join('test', 'passes', t)
+ actual = ''
+ for module, asserts in split_wast(t):
+ assert len(asserts) == 0
+ open('split.wast', 'w').write(module)
+ cmd = [os.path.join('bin', 'wasm-opt')] + opts + ['split.wast', '--print']
+ actual += run_command(cmd)
fail_if_not_identical(actual, open(os.path.join('test', 'passes', passname + '.txt')).read())
print '[ checking asm2wasm testcases... ]\n'
@@ -350,7 +304,7 @@ for asm in tests:
# verify in wasm
if interpreter:
# remove imports, spec interpreter doesn't know what to do with them
- subprocess.check_call([os.path.join('bin', 'wasm-shell'), '--remove-imports', '--print', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE)
+ subprocess.check_call([os.path.join('bin', 'wasm-opt'), '--remove-imports', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE)
proc = subprocess.Popen([interpreter, 'ztemp.wast'], stderr=subprocess.PIPE)
out, err = proc.communicate()
if proc.returncode != 0:
@@ -370,28 +324,28 @@ for asm in tests:
raise Exception('wasm interpreter error: ' + err) # failed to pretty-print
raise Exception('wasm interpreter error')
-print '\n[ checking wasm-shell parsing & printing... ]\n'
+print '\n[ checking wasm-opt parsing & printing... ]\n'
for t in sorted(os.listdir(os.path.join('test', 'print'))):
if t.endswith('.wast'):
print '..', t
wasm = os.path.basename(t).replace('.wast', '')
- cmd = [os.path.join('bin', 'wasm-shell'), os.path.join('test', 'print', t), '--print']
+ cmd = [os.path.join('bin', 'wasm-opt'), os.path.join('test', 'print', t), '--print']
print ' ', ' '.join(cmd)
actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
fail_if_not_identical(actual, open(os.path.join('test', 'print', wasm + '.txt')).read())
- cmd = [os.path.join('bin', 'wasm-shell'), os.path.join('test', 'print', t), '--print-minified']
+ cmd = [os.path.join('bin', 'wasm-opt'), os.path.join('test', 'print', t), '--print-minified']
print ' ', ' '.join(cmd)
actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
fail_if_not_identical(actual.strip(), open(os.path.join('test', 'print', wasm + '.minified.txt')).read().strip())
-print '\n[ checking wasm-shell testcases... ]\n'
+print '\n[ checking wasm-opt testcases... ]\n'
for t in tests:
if t.endswith('.wast') and not t.startswith('spec'):
print '..', t
t = os.path.join('test', t)
- cmd = [os.path.join('bin', 'wasm-shell'), t, '--print']
+ cmd = [os.path.join('bin', 'wasm-opt'), t, '--print']
actual = run_command(cmd)
actual = actual.replace('printing before:\n', '')
diff --git a/scripts/support.py b/scripts/support.py
index a99fb7604..2d4318980 100755
--- a/scripts/support.py
+++ b/scripts/support.py
@@ -87,3 +87,60 @@ def untar(tarfile, outdir):
finally:
if os.path.isdir(tmpdir):
shutil.rmtree(tmpdir)
+
+
+def split_wast(wast):
+ # .wast files can contain multiple modules, and assertions for each one.
+ # this splits out a wast into [(module, assertions), ..]
+ # we ignore module invalidity tests here.
+ wast = open(wast).read()
+ ret = []
+
+ def to_end(j):
+ depth = 1
+ while depth > 0 and j < len(wast):
+ if wast[j] == '"':
+ j = wast.find('"', j + 1)
+ elif wast[j] == '(':
+ depth += 1
+ elif wast[j] == ')':
+ depth -= 1
+ elif wast[j] == ';' and wast[j + 1] == ';':
+ j = wast.find('\n', j)
+ j += 1
+ return j
+
+ i = 0
+ while i >= 0:
+ start = wast.find('(', i)
+ if start >= 0 and wast[start + 1] == ';':
+ # block comment
+ i = wast.find(';)', start + 2)
+ assert i > 0, wast[start:]
+ i += 2
+ continue
+ skip = wast.find(';', i)
+ if skip >= 0 and skip < start and skip + 1 < len(wast):
+ if wast[skip + 1] == ';':
+ i = wast.find('\n', i) + 1
+ continue
+ if start < 0:
+ break
+ i = to_end(start + 1)
+ chunk = wast[start:i]
+ if chunk.startswith('(module'):
+ ret += [(chunk, [])]
+ elif chunk.startswith('(assert_invalid'):
+ continue
+ elif chunk.startswith(('(assert', '(invoke')):
+ ret[-1][1].append(chunk)
+ return ret
+
+
+def run_command(cmd, expected_status=0, stderr=None):
+ print 'executing: ', ' '.join(cmd)
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr)
+ out, err = proc.communicate()
+ if proc.returncode != expected_status:
+ raise Exception(('run_command failed', err))
+ return out
diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp
index e2425b24a..376c1288c 100644
--- a/src/tools/wasm-as.cpp
+++ b/src/tools/wasm-as.cpp
@@ -56,7 +56,7 @@ int main(int argc, const char *argv[]) {
Module wasm;
- try{
+ try {
if (options.debug) std::cerr << "s-parsing..." << std::endl;
SExpressionParser parser(const_cast<char*>(input.c_str()));
Element& root = *parser.root;
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
new file mode 100644
index 000000000..a992f77dd
--- /dev/null
+++ b/src/tools/wasm-opt.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+//
+// A WebAssembly optimizer, loads code, optionally runs passes on it,
+// then writes it.
+//
+
+#include <memory>
+
+#include "pass.h"
+#include "support/command-line.h"
+#include "support/file.h"
+#include "wasm-printing.h"
+#include "wasm-s-parser.h"
+#include "wasm-validator.h"
+
+using namespace wasm;
+
+//
+// main
+//
+
+int main(int argc, const char* argv[]) {
+ Name entry;
+ std::vector<std::string> passes;
+
+ Options options("wasm-opt", "Optimize .wast files");
+ options
+ .add("--output", "-o", "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["output"] = argument;
+ Colors::disable();
+ })
+ .add("", "-O", "execute default optimization passes",
+ Options::Arguments::Zero,
+ [&passes](Options*, const std::string&) {
+ passes.push_back("O");
+ })
+ .add_positional("INFILE", Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
+ for (const auto& p : PassRegistry::get()->getRegisteredNames()) {
+ options.add(
+ std::string("--") + p, "", PassRegistry::get()->getPassDescription(p),
+ Options::Arguments::Zero,
+ [&passes, p](Options*, const std::string&) { passes.push_back(p); });
+ }
+ options.parse(argc, argv);
+
+ auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
+
+ Module wasm;
+
+ try {
+ if (options.debug) std::cerr << "s-parsing..." << std::endl;
+ SExpressionParser parser(const_cast<char*>(input.c_str()));
+ Element& root = *parser.root;
+ if (options.debug) std::cerr << "w-parsing..." << std::endl;
+ SExpressionWasmBuilder builder(wasm, *root[0]);
+ assert(WasmValidator().validate(wasm));
+ } catch (ParseException& p) {
+ p.dump(std::cerr);
+ Fatal() << "error in parsing input";
+ }
+
+ if (passes.size() > 0) {
+ if (options.debug) std::cerr << "running passes...\n";
+ PassRunner passRunner(&wasm);
+ if (options.debug) passRunner.setDebug(true);
+ for (auto& passName : passes) {
+ if (passName == "O") {
+ passRunner.addDefaultOptimizationPasses();
+ } else {
+ passRunner.add(passName);
+ }
+ }
+ passRunner.run();
+ assert(WasmValidator().validate(wasm));
+ }
+
+ if (options.extra.count("output") > 0) {
+ if (options.debug) std::cerr << "writing..." << std::endl;
+ Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release);
+ WasmPrinter::printModule(&wasm, output.getStream());
+ }
+}
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 27fa6cd50..72e8e13ba 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -169,44 +169,21 @@ static void run_asserts(size_t* i, bool* checked, Module* wasm,
int main(int argc, const char* argv[]) {
Name entry;
- std::vector<std::string> passes;
Options options("wasm-shell", "Execute .wast files");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](Options* o, const std::string& argument) {
- o->extra["output"] = argument;
- Colors::disable();
- })
.add(
"--entry", "-e", "call the entry point after parsing the module",
Options::Arguments::One,
[&entry](Options*, const std::string& argument) { entry = argument; })
- .add("", "-O", "execute default optimization passes",
- Options::Arguments::Zero,
- [&passes](Options*, const std::string&) {
- passes.push_back("O");
- })
.add_positional("INFILE", Options::Arguments::One,
[](Options* o, const std::string& argument) {
o->extra["infile"] = argument;
});
- for (const auto& p : PassRegistry::get()->getRegisteredNames()) {
- options.add(
- std::string("--") + p, "", PassRegistry::get()->getPassDescription(p),
- Options::Arguments::Zero,
- [&passes, p](Options*, const std::string&) { passes.push_back(p); });
- }
options.parse(argc, argv);
auto input(read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
- std::unique_ptr<Output> output;
- if (options.extra.count("output") > 0) {
- output = wasm::make_unique<Output>(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release);
- }
-
bool checked = false;
try {
@@ -226,29 +203,7 @@ int main(int argc, const char* argv[]) {
builder = wasm::make_unique<SExpressionWasmBuilder>(wasm, *root[i]);
i++;
assert(WasmValidator().validate(wasm));
-
- MixedArena moreModuleAllocations;
-
- if (passes.size() > 0) {
- if (options.debug) std::cerr << "running passes...\n";
- PassRunner passRunner(&wasm);
- if (options.debug) passRunner.setDebug(true);
- for (auto& passName : passes) {
- if (passName == "O") {
- passRunner.addDefaultOptimizationPasses();
- } else {
- passRunner.add(passName);
- }
- }
- passRunner.run();
- assert(WasmValidator().validate(wasm));
- }
-
run_asserts(&i, &checked, &wasm, &root, &builder, entry);
-
- if (output) {
- WasmPrinter::printModule(&wasm, output->getStream());
- }
} else {
run_asserts(&i, &checked, nullptr, &root, nullptr, entry);
}
diff --git a/test/waterfall b/test/waterfall
-Subproject cba60ec0417d7d6a741ab42de0f46eee79e4f1e
+Subproject bc52e2063dd1bd06df64b7dfeceeb6e00437b90