diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/MergeBlocks.cpp | 77 | ||||
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 4 | ||||
-rw-r--r-- | src/passes/RemoveUnusedNames.cpp | 2 | ||||
-rw-r--r-- | src/wasm-binary.h | 1 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 37 |
5 files changed, 86 insertions, 35 deletions
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 7c086a920..619d7b5a5 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -154,6 +154,15 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> { } }; +static bool hasUnreachableChild(Block* block) { + for (auto* test : block->list) { + if (test->type == unreachable) { + return true; + } + } + return false; +} + // core block optimizer routine static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) { bool more = true; @@ -168,6 +177,11 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) if (drop) { child = drop->value->dynCast<Block>(); if (child) { + if (hasUnreachableChild(child)) { + // don't move around unreachable code, as it can change types + // dce should have been run anyhow + continue; + } if (child->name.is()) { Expression* expression = child; // check if it's ok to remove the value from all breaks to us @@ -200,15 +214,6 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) } if (!child) continue; if (child->name.is()) continue; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs) - if (child->type == unreachable) { - // an unreachable block can have a concrete final element (which is never reached) - if (!child->list.empty()) { - if (isConcreteWasmType(child->list.back()->type)) { - // just remove it - child->list.pop_back(); - } - } - } ExpressionList merged(module->allocator); for (size_t j = 0; j < i; j++) { merged.push_back(curr->list[j]); @@ -219,6 +224,16 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) for (size_t j = i + 1; j < curr->list.size(); j++) { merged.push_back(curr->list[j]); } + // if we merged a concrete element in the middle, drop it + if (!merged.empty()) { + auto* last = merged.back(); + for (auto*& item : merged) { + if (item != last && isConcreteWasmType(item->type)) { + Builder builder(*module); + item = builder.makeDrop(item); + } + } + } curr->list.swap(merged); more = true; changed = true; @@ -241,6 +256,23 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { optimizeBlock(curr, getModule(), getPassOptions()); } + // given + // (curr + // (block=child + // (..more..) + // (back) + // ) + // (..other..children..) + // ) + // if child is a block, we can move this around to + // (block + // (..more..) + // (curr + // (back) + // (..other..children..) + // ) + // ) + // at which point the block is on the outside and potentially mergeable with an outer block Block* optimize(Expression* curr, Expression*& child, Block* outer = nullptr, Expression** dependency1 = nullptr, Expression** dependency2 = nullptr) { if (!child) return outer; if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) { @@ -251,18 +283,29 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { } if (auto* block = child->dynCast<Block>()) { if (!block->name.is() && block->list.size() >= 2) { - child = block->list.back(); - // we modified child (which is a reference to a pointer), which modifies curr, which might change its type - // (e.g. (drop (block (result i32) .. (unreachable))) - // the child was a block of i32, and is being replaced with an unreachable, so the - // parent will likely need to be unreachable too - auto oldType = curr->type; - ReFinalize().walk(curr); + // if we move around unreachable code, type changes could occur. avoid that, as + // anyhow it means we should have run dce before getting here + if (curr->type == none && hasUnreachableChild(block)) { + // moving the block to the outside would replace a none with an unreachable + return outer; + } + auto* back = block->list.back(); + if (back->type == unreachable) { + // curr is not reachable, dce could remove it; don't try anything fancy + // here + return outer; + } + // we are going to replace the block with the final element, so they should + // be identically typed + if (block->type != back->type) { + return outer; + } + child = back; if (outer == nullptr) { // reuse the block, move it out block->list.back() = curr; // we want the block outside to have the same type as curr had - block->finalize(oldType); + block->finalize(curr->type); replaceCurrent(block); return block; } else { diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 9fed3e4a5..8994c7fe2 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -481,7 +481,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // format. We need to flip the condition, which at worst adds 1. if (curr->name.is()) { auto* br = list[0]->dynCast<Break>(); - if (br && br->condition && br->name == curr->name) { + // we seek a regular br_if; if the type is unreachable that means it is not + // actually taken, so ignore + if (br && br->condition && br->name == curr->name && br->type != unreachable) { assert(!br->value); // can't, it would be dropped or last in the block if (BranchUtils::BranchSeeker::countNamed(curr, curr->name) == 1) { // no other breaks to that name, so we can do this diff --git a/src/passes/RemoveUnusedNames.cpp b/src/passes/RemoveUnusedNames.cpp index 39bf068df..e5aaf5509 100644 --- a/src/passes/RemoveUnusedNames.cpp +++ b/src/passes/RemoveUnusedNames.cpp @@ -57,7 +57,7 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> { void visitBlock(Block *curr) { if (curr->name.is() && curr->list.size() == 1) { auto* child = curr->list[0]->dynCast<Block>(); - if (child && child->name.is()) { + if (child && child->name.is() && child->type == curr->type) { // we have just one child, this block, so breaking out of it goes to the same place as breaking out of us, we just need one name (and block) auto& branches = branchesSeen[curr->name]; for (auto* branch : branches) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index ca3d69989..9c468b0ea 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -861,6 +861,7 @@ public: int depth = 0; // only for debugging BinaryConsts::ASTNodes readExpression(Expression*& curr); + void pushBlockElements(Block* curr, size_t start, size_t end); void visitBlock(Block *curr); Expression* getMaybeBlock(WasmType type); Expression* getBlock(WasmType type); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 47bcc8ba5..63dddbdd6 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -583,6 +583,11 @@ void WasmBinaryWriter::recursePossibleBlockContents(Expression* curr) { for (auto* child : block->list) { recurse(child); } + if (block->type == unreachable && block->list.back()->type != unreachable) { + // similar to in visitBlock, here we could skip emitting the block itself, + // but must still end the 'block' (the contents, really) with an unreachable + o << int8_t(BinaryConsts::Unreachable); + } } void WasmBinaryWriter::visitIf(If *curr) { @@ -2073,6 +2078,20 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { return BinaryConsts::ASTNodes(code); } +void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + auto* item = expressionStack[i]; + curr->list.push_back(item); + if (i < end - 1) { + // stacky&unreachable code may introduce elements that need to be dropped in non-final positions + if (isConcreteWasmType(item->type)) { + curr->list.back() = Builder(wasm).makeDrop(curr->list.back()); + } + } + } + expressionStack.resize(start); +} + void WasmBinaryBuilder::visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; // special-case Block and de-recurse nested blocks in their first position, as that is @@ -2108,18 +2127,7 @@ void WasmBinaryBuilder::visitBlock(Block *curr) { if (end < start) { throw ParseException("block cannot pop from outside"); } - for (size_t i = start; i < end; i++) { - if (debug) std::cerr << " " << size_t(expressionStack[i]) << "\n zz Block element " << curr->list.size() << std::endl; - auto* item = expressionStack[i]; - curr->list.push_back(item); - if (i < end - 1) { - // stacky&unreachable code may introduce elements that need to be dropped in non-final positions - if (isConcreteWasmType(item->type)) { - curr->list.back() = Builder(wasm).makeDrop(curr->list.back()); - } - } - } - expressionStack.resize(start); + pushBlockElements(curr, start, end); curr->finalize(curr->type); breakStack.pop_back(); } @@ -2136,11 +2144,8 @@ Expression* WasmBinaryBuilder::getMaybeBlock(WasmType type) { throw ParseException("block cannot pop from outside"); } auto* block = allocator.alloc<Block>(); - for (size_t i = start; i < end; i++) { - block->list.push_back(expressionStack[i]); - } + pushBlockElements(block, start, end); block->finalize(type); - expressionStack.resize(start); return block; } |