diff options
author | Alon Zakai <alonzakai@gmail.com> | 2017-02-16 14:27:23 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-16 14:27:23 -0800 |
commit | 5e29a613bb8bf059f5f7b9ec2f166f8d2e413ac5 (patch) | |
tree | 64eb41cbef50830367255672091c62fd10724ce2 /src | |
parent | 97077693a6458d51bd5b3dc85187d5a4bd16c3ee (diff) | |
download | binaryen-5e29a613bb8bf059f5f7b9ec2f166f8d2e413ac5.tar.gz binaryen-5e29a613bb8bf059f5f7b9ec2f166f8d2e413ac5.tar.bz2 binaryen-5e29a613bb8bf059f5f7b9ec2f166f8d2e413ac5.zip |
Fix emitting of unreachable block/if/loop (#911)
* an unreachable block is one with an unreachable child, plus no breaks
* document new difference between binaryen IR and wasm
* fix relooper missing finalize
* add a bunch of tests
* don't assume that test/*.wast files print to themselves exactly; print to from.wast. this allows wast tests with comments in them
* emit unreachable blocks as (block .. unreachable) unreachable
* if without else and unreachable ifTrue is still not unreachable, it should be none
* update wasm.js
* cleanups
* empty blocks have none type
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg/Relooper.cpp | 1 | ||||
-rw-r--r-- | src/wasm-validator.h | 7 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 40 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 31 |
4 files changed, 69 insertions, 10 deletions
diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp index d412c7b31..f6250b888 100644 --- a/src/cfg/Relooper.cpp +++ b/src/cfg/Relooper.cpp @@ -90,6 +90,7 @@ static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* P } } } + Curr->finalize(); return Curr; } diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 28e575ca4..dacba39c3 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -128,7 +128,9 @@ public: } } if (!isConcreteWasmType(curr->type) && curr->list.size() > 0) { - shouldBeFalse(isConcreteWasmType(curr->list.back()->type), curr, "block with no value cannot have a last element with a value"); + if (isConcreteWasmType(curr->list.back()->type)) { + shouldBeTrue(curr->type == unreachable, curr, "block with no value and a last element with a value must be unreachable"); + } } } @@ -155,6 +157,9 @@ public: shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32 || curr->condition->type == i64, curr, "if condition must be valid"); if (!curr->ifFalse) { shouldBeFalse(isConcreteWasmType(curr->ifTrue->type), curr, "if without else must not return a value in body"); + if (curr->condition->type != unreachable) { + shouldBeEqual(curr->type, none, curr, "if without else and reachable condition must be none"); + } } } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 6b5878904..456e4505e 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -461,24 +461,39 @@ void WasmBinaryWriter::recurse(Expression*& curr) { if (debug) std::cerr << "zz recurse from " << depth-- << " at " << o.size() << std::endl; } +static bool brokenTo(Block* block) { + return block->name.is() && BreakSeeker::has(block, block->name); +} + void WasmBinaryWriter::visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; o << int8_t(BinaryConsts::Block); o << binaryWasmType(curr->type != unreachable ? curr->type : none); breakStack.push_back(curr->name); - size_t i = 0; + Index i = 0; for (auto* child : curr->list) { if (debug) std::cerr << " " << size_t(curr) << "\n zz Block element " << i++ << std::endl; recurse(child); } breakStack.pop_back(); + if (curr->type == unreachable) { + // an unreachable block is one that cannot be exited. We cannot encode this directly + // in wasm, where blocks must be none,i32,i64,f32,f64. Since the block cannot be + // exited, we can emit an unreachable at the end, and that will always be valid, + // and then the block is ok as a none + o << int8_t(BinaryConsts::Unreachable); + } o << int8_t(BinaryConsts::End); + if (curr->type == unreachable) { + // and emit an unreachable *outside* the block too, so later things can pop anything + o << int8_t(BinaryConsts::Unreachable); + } } // emits a node, but if it is a block with no name, emit a list of its contents void WasmBinaryWriter::recursePossibleBlockContents(Expression* curr) { auto* block = curr->dynCast<Block>(); - if (!block || (block->name.is() && BreakSeeker::has(curr, block->name))) { + if (!block || brokenTo(block)) { recurse(curr); return; } @@ -489,6 +504,18 @@ void WasmBinaryWriter::recursePossibleBlockContents(Expression* curr) { void WasmBinaryWriter::visitIf(If *curr) { if (debug) std::cerr << "zz node: If" << std::endl; + if (curr->type == unreachable && curr->ifFalse) { + if (curr->condition->type == unreachable) { + // this if-else is unreachable because of the condition, i.e., the condition + // does not exit. So don't emit the if, but do consume the condition + recurse(curr->condition); + o << int8_t(BinaryConsts::Unreachable); + return; + } + // an unreachable if-else (with reachable condition) is one where both sides do not fall through. + // wasm does not allow this to be emitted directly, so we must do something more. we could do + // better, but for now we emit an extra unreachable instruction after the if, so it is not consumed itself + } recurse(curr->condition); o << int8_t(BinaryConsts::If); o << binaryWasmType(curr->type != unreachable ? curr->type : none); @@ -502,6 +529,11 @@ void WasmBinaryWriter::visitIf(If *curr) { breakStack.pop_back(); } o << int8_t(BinaryConsts::End); + if (curr->type == unreachable) { + // see explanation above - we emitted an if without a return type, so it must not be consumed + assert(curr->ifFalse); // otherwise, if without else, that is unreachable, must have an unreachable condition, which was handled earlier + o << int8_t(BinaryConsts::Unreachable); + } } void WasmBinaryWriter::visitLoop(Loop *curr) { if (debug) std::cerr << "zz node: Loop" << std::endl; @@ -511,6 +543,10 @@ void WasmBinaryWriter::visitLoop(Loop *curr) { recursePossibleBlockContents(curr->body); breakStack.pop_back(); o << int8_t(BinaryConsts::End); + if (curr->type == unreachable) { + // we emitted a loop without a return type, so it must not be consumed + o << int8_t(BinaryConsts::Unreachable); + } } int32_t WasmBinaryWriter::getBreakIndex(Name name) { // -1 if not found diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index cf58949de..c80d10c83 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -133,14 +133,30 @@ static WasmType mergeTypes(std::vector<WasmType>& types) { return type; } +// a block is unreachable if one of its elements is unreachable, +// and there are no branches to it +static void handleUnreachable(Block* block) { + if (block->type == unreachable) return; // nothing to do + for (auto* child : block->list) { + if (child->type == unreachable) { + // there is an unreachable child, so we are unreachable, unless we have a break + BreakSeeker seeker(block->name); + Expression* expr = block; + seeker.walk(expr); + if (!seeker.found) { + block->type = unreachable; + } else { + block->type = seeker.valueType; + } + return; + } + } +} + void Block::finalize(WasmType type_) { type = type_; if (type == none && list.size() > 0) { - if (list.back()->type == unreachable) { - if (!BreakSeeker::has(this, name)) { - type = unreachable; // the last element is unreachable, and this block truly cannot be exited, so it is unreachable itself - } - } + handleUnreachable(this); } } @@ -150,18 +166,19 @@ void Block::finalize() { if (list.size() > 0) { type = list.back()->type; } else { - type = unreachable; + type = none; } return; } TypeSeeker seeker(this, this->name); type = mergeTypes(seeker.types); + handleUnreachable(this); } void If::finalize(WasmType type_) { type = type_; - if (type == none && (condition->type == unreachable || (ifTrue->type == unreachable && (!ifFalse || ifFalse->type == unreachable)))) { + if (type == none && (condition->type == unreachable || (ifFalse && ifTrue->type && ifFalse->type == unreachable))) { type = unreachable; } } |