diff options
-rwxr-xr-x | auto_update_tests.py | 7 | ||||
-rwxr-xr-x | check.py | 7 | ||||
-rwxr-xr-x | scripts/test/support.py | 5 | ||||
-rw-r--r-- | src/asm2wasm.h | 1 | ||||
-rw-r--r-- | src/wasm-builder.h | 6 | ||||
-rw-r--r-- | src/wasm.h | 14 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 11 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 7 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 61 | ||||
-rw-r--r-- | test/passes/O.bin.txt | 117 | ||||
-rw-r--r-- | test/passes/O.wasm | bin | 0 -> 330 bytes | |||
-rw-r--r-- | test/passes/dce.txt | 51 | ||||
-rw-r--r-- | test/passes/dce_vacuum.bin.txt | 101 | ||||
-rw-r--r-- | test/passes/dce_vacuum.txt | 19 | ||||
-rw-r--r-- | test/passes/dce_vacuum.wasm | bin | 0 -> 260 bytes | |||
-rw-r--r-- | test/passes/dce_vacuum.wast | 37 |
16 files changed, 403 insertions, 41 deletions
diff --git a/auto_update_tests.py b/auto_update_tests.py index 0c07001e2..d76d08750 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -84,9 +84,10 @@ for t in sorted(os.listdir(os.path.join('test', 'print'))): with open(os.path.join('test', 'print', wasm + '.minified.txt'), 'w') as o: o.write(actual) for t in sorted(os.listdir(os.path.join('test', 'passes'))): - if t.endswith('.wast'): + if t.endswith(('.wast', '.wasm')): print '..', t - passname = os.path.basename(t).replace('.wast', '') + binary = '.wasm' in t + passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '') opts = ['-' + passname] if passname.startswith('O') else ['--' + p for p in passname.split('_')] t = os.path.join('test', 'passes', t) actual = '' @@ -95,7 +96,7 @@ for t in sorted(os.listdir(os.path.join('test', 'passes'))): with open('split.wast', 'w') as o: o.write(module) cmd = WASM_OPT + opts + ['split.wast', '--print'] actual += run_command(cmd) - with open(os.path.join('test', 'passes', passname + '.txt'), 'w') as o: o.write(actual) + with open(os.path.join('test', 'passes', passname + ('.bin' if binary else '') + '.txt'), 'w') as o: o.write(actual) print '\n[ checking wasm-opt -o notation... ]\n' @@ -74,9 +74,10 @@ assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S' print '\n[ checking wasm-opt passes... ]\n' for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'passes'))): - if t.endswith('.wast'): + if t.endswith(('.wast', '.wasm')): print '..', t - passname = os.path.basename(t).replace('.wast', '') + binary = '.wasm' in t + passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '') opts = ['-' + passname] if passname.startswith('O') else ['--' + p for p in passname.split('_')] t = os.path.join(options.binaryen_test, 'passes', t) actual = '' @@ -88,7 +89,7 @@ for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'passes'))): # also check debug mode output is valid debugged = run_command(cmd + ['--debug'], stderr=subprocess.PIPE) fail_if_not_contained(actual, debugged) - fail_if_not_identical(actual, open(os.path.join(options.binaryen_test, 'passes', passname + '.txt'), 'rb').read()) + fail_if_not_identical(actual, open(os.path.join('test', 'passes', passname + ('.bin' if binary else '') + '.txt'), 'rb').read()) print '[ checking asm2wasm testcases... ]\n' diff --git a/scripts/test/support.py b/scripts/test/support.py index 43762fffa..5d791d155 100755 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -94,6 +94,11 @@ def split_wast(wast): # this splits out a wast into [(module, assertions), ..] # we ignore module invalidity tests here. wast = open(wast).read() + + # if it's a binary, leave it as is + if wast[0] == '\0': + return [[wast, '']] + ret = [] def to_end(j): diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 00481c9bd..2fbe4d0ae 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -2296,6 +2296,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Break *continuer = allocator.alloc<Break>(); continuer->name = in; continuer->condition = process(ast[1]); + continuer->finalize(); Block *block = builder.blockifyWithName(loop->body, out, continuer); loop->body = block; loop->finalize(); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 6b3f1a7d7..61db3c9e8 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -288,7 +288,7 @@ public: if (!block) block = makeBlock(any); if (append) { block->list.push_back(append); - block->finalize(); // TODO: move out of if + block->finalize(); } return block; } @@ -302,7 +302,7 @@ public: block->name = name; if (append) { block->list.push_back(append); - block->finalize(); // TODO: move out of if + block->finalize(); } return block; } @@ -325,7 +325,7 @@ public: block->list.push_back(item); } } - block->finalize(); // TODO: move out of if + block->finalize(); return block; } diff --git a/src/wasm.h b/src/wasm.h index b23dab918..3caab9dbc 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -309,6 +309,8 @@ public: ExpressionList operands; Name target; + + void finalize(); }; class CallImport : public SpecificExpression<Expression::CallImportId> { @@ -317,6 +319,8 @@ public: ExpressionList operands; Name target; + + void finalize(); }; class FunctionType { @@ -340,6 +344,8 @@ public: ExpressionList operands; Name fullType; Expression* target; + + void finalize(); }; class GetLocal : public SpecificExpression<Expression::GetLocalId> { @@ -355,6 +361,8 @@ public: SetLocal() {} SetLocal(MixedArena& allocator) {} + void finalize(); + Index index; Expression* value; @@ -377,6 +385,8 @@ public: Name name; Expression* value; + + void finalize(); }; class Load : public SpecificExpression<Expression::LoadId> { @@ -391,6 +401,8 @@ public: Expression* ptr; // type must be set during creation, cannot be inferred + + void finalize(); }; class Store : public SpecificExpression<Expression::StoreId> { @@ -466,6 +478,8 @@ public: Drop(MixedArena& allocator) {} Expression* value; + + void finalize(); }; class Return : public SpecificExpression<Expression::ReturnId> { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 159099a16..6448615a5 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -676,7 +676,8 @@ void WasmBinaryWriter::visitLoad(Load *curr) { } case f32: o << int8_t(BinaryConsts::F32LoadMem); break; case f64: o << int8_t(BinaryConsts::F64LoadMem); break; - default: abort(); + case unreachable: return; // the pointer is unreachable, so we are never reached; just don't emit a load + default: WASM_UNREACHABLE(); } emitMemoryAccess(curr->align, curr->bytes, curr->offset); } @@ -1732,7 +1733,6 @@ Expression* WasmBinaryBuilder::getBlock(WasmType type) { Name label = getNextLabel(); breakStack.push_back({label, type != none && type != unreachable}); auto* block = Builder(wasm).blockify(getMaybeBlock(type)); - block->finalize(); breakStack.pop_back(); block->cast<Block>()->name = label; return block; @@ -1814,6 +1814,7 @@ Expression* WasmBinaryBuilder::visitCall() { call->target = import->name; type = wasm.getFunctionType(import->functionType); fillCall(call, type); + call->finalize(); ret = call; } else { // this is a call of a defined function @@ -1825,6 +1826,7 @@ Expression* WasmBinaryBuilder::visitCall() { type = functionTypes[adjustedIndex]; fillCall(call, type); functionCalls[adjustedIndex].push_back(call); // we don't know function names yet + call->finalize(); ret = call; } return ret; @@ -1847,6 +1849,7 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect *curr) { curr->operands[num - i - 1] = popNonVoidExpression(); } curr->type = fullType->result; + curr->finalize(); } void WasmBinaryBuilder::visitGetLocal(GetLocal *curr) { @@ -1873,6 +1876,7 @@ void WasmBinaryBuilder::visitSetLocal(SetLocal *curr, uint8_t code) { curr->value = popNonVoidExpression(); curr->type = curr->value->type; curr->setTee(code == BinaryConsts::TeeLocal); + curr->finalize(); } void WasmBinaryBuilder::visitGetGlobal(GetGlobal *curr) { @@ -1897,6 +1901,7 @@ void WasmBinaryBuilder::visitSetGlobal(SetGlobal *curr) { auto index = getU32LEB(); curr->name = getGlobalName(index); curr->value = popNonVoidExpression(); + curr->finalize(); } void WasmBinaryBuilder::readMemoryAccess(Address& alignment, size_t bytes, Address& offset) { @@ -1926,6 +1931,7 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code) { if (debug) std::cerr << "zz node: Load" << std::endl; readMemoryAccess(curr->align, curr->bytes, curr->offset); curr->ptr = popNonVoidExpression(); + curr->finalize(); out = curr; return true; } @@ -2149,6 +2155,7 @@ void WasmBinaryBuilder::visitUnreachable(Unreachable *curr) { void WasmBinaryBuilder::visitDrop(Drop *curr) { if (debug) std::cerr << "zz node: Drop" << std::endl; curr->value = popNonVoidExpression(); + curr->finalize(); } } // namespace wasm diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 69509afec..f9e3912f7 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -928,6 +928,7 @@ Expression* SExpressionWasmBuilder::makeTeeLocal(Element& s) { ret->index = getLocalIndex(*s[1]); ret->value = parseExpression(s[2]); ret->setTee(true); + ret->finalize(); return ret; } @@ -936,6 +937,7 @@ Expression* SExpressionWasmBuilder::makeSetLocal(Element& s) { ret->index = getLocalIndex(*s[1]); ret->value = parseExpression(s[2]); ret->setTee(false); + ret->finalize(); return ret; } @@ -960,6 +962,7 @@ Expression* SExpressionWasmBuilder::makeSetGlobal(Element& s) { ret->name = getGlobalName(*s[1]); if (wasm.getGlobalOrNull(ret->name) && !wasm.getGlobalOrNull(ret->name)->mutable_) throw ParseException("set_global of immutable", s.line, s.col); ret->value = parseExpression(s[2]); + ret->finalize(); return ret; } @@ -1084,6 +1087,7 @@ Expression* SExpressionWasmBuilder::makeLoad(Element& s, WasmType type) { i++; } ret->ptr = parseExpression(s[i]); + ret->finalize(); return ret; } @@ -1210,6 +1214,7 @@ Expression* SExpressionWasmBuilder::makeCall(Element& s) { ret->target = target; ret->type = functionTypes[ret->target]; parseCallOperands(s, 2, s.size(), ret); + ret->finalize(); return ret; } @@ -1219,6 +1224,7 @@ Expression* SExpressionWasmBuilder::makeCallImport(Element& s) { Import* import = wasm.getImport(ret->target); ret->type = wasm.getFunctionType(import->functionType)->result; parseCallOperands(s, 2, s.size(), ret); + ret->finalize(); return ret; } @@ -1232,6 +1238,7 @@ Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s) { ret->type = fullType->result; parseCallOperands(s, 2, s.size() - 1, ret); ret->target = parseExpression(s[s.size() - 1]); + ret->finalize(); return ret; } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 96ca8fbc8..834d9e28f 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -198,6 +198,16 @@ void Block::finalize() { if (!name.is()) { // nothing branches here, so this is easy if (list.size() > 0) { + // if we have an unreachable child, we are unreachable + // (we don't need to recurse into children, they can't + // break to us) + for (auto* child : list) { + if (child->type == unreachable) { + type = unreachable; + return; + } + } + // children are reachable, so last element determines type type = list.back()->type; } else { type = none; @@ -264,6 +274,31 @@ void Switch::finalize() { type = unreachable; } +template<typename T> +void handleUnreachableOperands(T* curr) { + for (auto* child : curr->operands) { + if (child->type == unreachable) { + curr->type = unreachable; + break; + } + } +} + +void Call::finalize() { + handleUnreachableOperands(this); +} + +void CallImport::finalize() { + handleUnreachableOperands(this); +} + +void CallIndirect::finalize() { + handleUnreachableOperands(this); + if (target->type == unreachable) { + type = unreachable; + } +} + bool FunctionType::structuralComparison(FunctionType& b) { if (result != b.result) return false; if (params.size() != b.params.size()) return false; @@ -290,6 +325,24 @@ void SetLocal::setTee(bool is) { else type = none; } +void SetLocal::finalize() { + if (value->type == unreachable) { + type = unreachable; + } +} + +void SetGlobal::finalize() { + if (value->type == unreachable) { + type = unreachable; + } +} + +void Load::finalize() { + if (ptr->type == unreachable) { + type = unreachable; + } +} + void Store::finalize() { assert(valueType != none); // must be set if (ptr->type == unreachable || value->type == unreachable) { @@ -423,6 +476,14 @@ void Select::finalize() { } } +void Drop::finalize() { + if (value->type == unreachable) { + type = unreachable; + } else { + type = none; + } +} + void Host::finalize() { switch (op) { case PageSize: case CurrentMemory: case HasFeature: { diff --git a/test/passes/O.bin.txt b/test/passes/O.bin.txt new file mode 100644 index 000000000..395c04369 --- /dev/null +++ b/test/passes/O.bin.txt @@ -0,0 +1,117 @@ +(module + (type $0 (func (param i64) (result i64))) + (memory $0 0) + (export "fac-rec" (func $0)) + (export "fac-rec-named" (func $1)) + (export "fac-iter" (func $2)) + (export "fac-iter-named" (func $3)) + (export "fac-opt" (func $4)) + (func $0 (type $0) (param $0 i64) (result i64) + (if i64 + (i64.eq + (get_local $0) + (i64.const 0) + ) + (i64.const 1) + (i64.mul + (get_local $0) + (call $0 + (i64.sub + (get_local $0) + (i64.const 1) + ) + ) + ) + ) + ) + (func $1 (type $0) (param $0 i64) (result i64) + (if i64 + (i64.eq + (get_local $0) + (i64.const 0) + ) + (i64.const 1) + (i64.mul + (get_local $0) + (call $1 + (i64.sub + (get_local $0) + (i64.const 1) + ) + ) + ) + ) + ) + (func $2 (type $0) (param $0 i64) (result i64) + (if + (i64.eq + (get_local $0) + (i64.const 0) + ) + (unreachable) + (unreachable) + ) + ) + (func $3 (type $0) (param $0 i64) (result i64) + (local $1 i64) + (set_local $1 + (i64.const 1) + ) + (block $label$1 + (loop $label$2 + (br_if $label$1 + (i64.eq + (get_local $0) + (i64.const 0) + ) + ) + (set_local $1 + (i64.mul + (get_local $0) + (get_local $1) + ) + ) + (set_local $0 + (i64.sub + (get_local $0) + (i64.const 1) + ) + ) + (br $label$2) + ) + ) + (get_local $1) + ) + (func $4 (type $0) (param $0 i64) (result i64) + (local $1 i64) + (set_local $1 + (i64.const 1) + ) + (if + (i64.ge_s + (get_local $0) + (i64.const 2) + ) + (loop $label$2 + (set_local $1 + (i64.mul + (get_local $1) + (get_local $0) + ) + ) + (br_if $label$2 + (i64.gt_s + (tee_local $0 + (i64.add + (get_local $0) + (i64.const -1) + ) + ) + (i64.const 1) + ) + ) + ) + ) + (get_local $1) + ) +) diff --git a/test/passes/O.wasm b/test/passes/O.wasm Binary files differnew file mode 100644 index 000000000..6dfd301de --- /dev/null +++ b/test/passes/O.wasm diff --git a/test/passes/dce.txt b/test/passes/dce.txt index 98f0ea177..3ccdbdffd 100644 --- a/test/passes/dce.txt +++ b/test/passes/dce.txt @@ -361,22 +361,15 @@ ) ) (func $unreachable-brs-4 (type $3) (param $var$0 i32) (result i32) - (i32.add + (drop (i32.const 1) - (block $label$0 i32 - (br $label$0 - (block $label$1 i32 - (drop - (block - (drop - (i32.const 4104) - ) - (unreachable) - ) - ) - (unreachable) - ) + ) + (drop + (block + (drop + (i32.const 4104) ) + (unreachable) ) ) ) @@ -389,26 +382,24 @@ (block $label$0 i64 (get_local $var$1) ) - (block $label$1 i64 - (block i64 - (drop - (i64.sub - (get_local $var$0) - (i64.const 1) - ) + (block + (drop + (i64.sub + (get_local $var$0) + (i64.const 1) ) - (block - (drop - (block $block i64 - (set_local $2 - (get_local $var$0) - ) - (nop) - (get_local $2) + ) + (block + (drop + (block $block i64 + (set_local $2 + (get_local $var$0) ) + (nop) + (get_local $2) ) - (unreachable) ) + (unreachable) ) ) ) diff --git a/test/passes/dce_vacuum.bin.txt b/test/passes/dce_vacuum.bin.txt new file mode 100644 index 000000000..8d365b327 --- /dev/null +++ b/test/passes/dce_vacuum.bin.txt @@ -0,0 +1,101 @@ +(module + (type $0 (func (param f32 f32) (result f32))) + (type $1 (func (param f64 f64) (result f64))) + (memory $0 0) + (export "f32.compute_radix" (func $0)) + (export "f64.compute_radix" (func $1)) + (func $0 (type $0) (param $var$0 f32) (param $var$1 f32) (result f32) + (block $label$0 f32 + (loop $label$1 + (br_if $label$1 + (f32.eq + (f32.add + (f32.sub + (f32.add + (tee_local $var$0 + (f32.add + (get_local $var$0) + (get_local $var$0) + ) + ) + (f32.const 1) + ) + (get_local $var$0) + ) + (f32.const -1) + ) + (f32.const 0) + ) + ) + ) + (drop + (block + (drop + (call $0 + (f32.add + (get_local $var$0) + (tee_local $var$1 + (f32.add + (get_local $var$1) + (f32.const 1) + ) + ) + ) + (get_local $var$0) + ) + ) + (unreachable) + ) + ) + ) + ) + (func $1 (type $1) (param $var$0 f64) (param $var$1 f64) (result f64) + (block $label$0 f64 + (loop $label$1 + (br_if $label$1 + (f64.eq + (f64.add + (f64.sub + (f64.add + (tee_local $var$0 + (f64.add + (get_local $var$0) + (get_local $var$0) + ) + ) + (f64.const 1) + ) + (get_local $var$0) + ) + (f64.const -1) + ) + (f64.const 0) + ) + ) + ) + (loop $label$2 + (br_if $label$2 + (f64.ne + (f64.sub + (f64.sub + (f64.add + (get_local $var$0) + (tee_local $var$1 + (f64.add + (get_local $var$1) + (f64.const 1) + ) + ) + ) + (get_local $var$0) + ) + (get_local $var$1) + ) + (f64.const 0) + ) + ) + ) + (get_local $var$1) + ) + ) +) diff --git a/test/passes/dce_vacuum.txt b/test/passes/dce_vacuum.txt index 4fb982524..0f1ec5095 100644 --- a/test/passes/dce_vacuum.txt +++ b/test/passes/dce_vacuum.txt @@ -1,9 +1,28 @@ (module (type $0 (func (result i32))) + (type $1 (func (param f32 f32) (result f32))) + (type $2 (func (param i64) (result i64))) (memory $0 0) (func $__Z12serveroptionPc (type $0) (result i32) (return (i32.const 0) ) ) + (func $drop-unreachable (type $1) (param $var$0 f32) (param $var$1 f32) (result f32) + (drop + (unreachable) + ) + ) + (func $set-unreachable (type $2) (param $var$0 i64) (result i64) + (local $var$1 i64) + (local $var$2 i64) + (if + (i64.eq + (get_local $var$1) + (i64.const 0) + ) + (unreachable) + (unreachable) + ) + ) ) diff --git a/test/passes/dce_vacuum.wasm b/test/passes/dce_vacuum.wasm Binary files differnew file mode 100644 index 000000000..5fa1892bd --- /dev/null +++ b/test/passes/dce_vacuum.wasm diff --git a/test/passes/dce_vacuum.wast b/test/passes/dce_vacuum.wast index a7e43db23..acddd76e9 100644 --- a/test/passes/dce_vacuum.wast +++ b/test/passes/dce_vacuum.wast @@ -10,5 +10,42 @@ (i32.const 0) ) ) + (func $drop-unreachable (param $var$0 f32) (param $var$1 f32) (result f32) + (block $label$0 f32 + (loop $label$2 + (drop + (unreachable) + ) + (unreachable) + ) + (get_local $var$1) + ) + ) + + (func $set-unreachable (param $var$0 i64) (result i64) + (local $var$1 i64) + (local $var$2 i64) + (block $label$0 i64 + (block $label$1 + (loop $label$2 + (if + (i64.eq + (get_local $var$1) + (i64.const 0) + ) + (unreachable) + (set_local $var$2 + (i64.mul + (unreachable) + (get_local $var$2) + ) + ) + ) + (br $label$2) + ) + ) + (get_local $var$2) + ) + ) ) |