summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/MergeBlocks.cpp77
-rw-r--r--src/passes/RemoveUnusedBrs.cpp4
-rw-r--r--src/passes/RemoveUnusedNames.cpp2
-rw-r--r--src/wasm-binary.h1
-rw-r--r--src/wasm/wasm-binary.cpp37
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;
}