Skip to content

Commit ec38a51

Browse files
authored
Fix a merge-blocks fuzz bug (#1755)
* Moving blocks into if arms may change the block type, and the code we had was written under the assumption that was not true. * Move block sinking merge-blocks => remove-unused-brs, as it's more natural there. that pass refinalizes everything anyhow
1 parent 7011e47 commit ec38a51

22 files changed

+6729
-6473
lines changed

src/passes/MergeBlocks.cpp

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -335,62 +335,6 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
335335
Pass* create() override { return new MergeBlocks; }
336336

337337
void visitBlock(Block *curr) {
338-
// If the block has a single child which is a loop, and the block is named,
339-
// then it is the exit for the loop. It's better to move it into the loop,
340-
// where it can be better optimized by other passes.
341-
// Similar logic for ifs: if the block is an exit for the if, we can
342-
// move the block in, consider for example:
343-
// (block $label
344-
// (if (..condition1..)
345-
// (block
346-
// (br_if $label (..condition2..))
347-
// (..code..)
348-
// )
349-
// )
350-
// )
351-
// After also merging the blocks, we have
352-
// (if (..condition1..)
353-
// (block $label
354-
// (br_if $label (..condition2..))
355-
// (..code..)
356-
// )
357-
// )
358-
// which can be further optimized by other passes.
359-
if (curr->name.is() && curr->list.size() == 1) {
360-
if (auto* loop = curr->list[0]->dynCast<Loop>()) {
361-
curr->list[0] = loop->body;
362-
loop->body = curr;
363-
auto oldOuterType = curr->type;
364-
curr->finalize(curr->type);
365-
loop->finalize();
366-
// After the flip, the outer type must be the same
367-
assert(loop->type == oldOuterType);
368-
replaceCurrent(loop);
369-
} else if (auto* iff = curr->list[0]->dynCast<If>()) {
370-
// The label can't be used in the condition.
371-
if (BranchUtils::BranchSeeker::countNamed(iff->condition, curr->name) == 0) {
372-
// We can move the block into either arm, if there are no uses in the other.
373-
Expression** target = nullptr;
374-
if (!iff->ifFalse ||
375-
BranchUtils::BranchSeeker::countNamed(iff->ifFalse, curr->name) == 0) {
376-
target = &iff->ifTrue;
377-
} else if (BranchUtils::BranchSeeker::countNamed(iff->ifTrue, curr->name) == 0) {
378-
target = &iff->ifFalse;
379-
}
380-
if (target) {
381-
curr->list[0] = *target;
382-
*target = curr;
383-
curr->finalize(curr->type);
384-
iff->finalize();
385-
replaceCurrent(iff);
386-
// Note that the type might change, e.g. if the if condition is unreachable
387-
// but the block that was on the outside had a break.
388-
}
389-
}
390-
}
391-
// Always fall through to optimize the block, which has a new child now.
392-
}
393-
// Otherwise, do the main merging optimizations.
394338
optimizeBlock(curr, getModule(), getPassOptions());
395339
}
396340

src/passes/RemoveUnusedBrs.cpp

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
102102
// if without else stops the flow of values
103103
self->stopValueFlow();
104104
}
105-
} else if (curr->is<Block>()) {
105+
} else if (auto* block = curr->dynCast<Block>()) {
106106
// any breaks flowing to here are unnecessary, as we get here anyhow
107-
auto* block = curr->cast<Block>();
108107
auto name = block->name;
109108
if (name.is()) {
110109
size_t size = flows.size();
@@ -436,6 +435,76 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
436435
}
437436
}
438437

438+
void sinkBlocks(Function* func) {
439+
struct Sinker : public PostWalker<Sinker> {
440+
bool worked = false;
441+
442+
void visitBlock(Block* curr) {
443+
// If the block has a single child which is a loop, and the block is named,
444+
// then it is the exit for the loop. It's better to move it into the loop,
445+
// where it can be better optimized by other passes.
446+
// Similar logic for ifs: if the block is an exit for the if, we can
447+
// move the block in, consider for example:
448+
// (block $label
449+
// (if (..condition1..)
450+
// (block
451+
// (br_if $label (..condition2..))
452+
// (..code..)
453+
// )
454+
// )
455+
// )
456+
// After also merging the blocks, we have
457+
// (if (..condition1..)
458+
// (block $label
459+
// (br_if $label (..condition2..))
460+
// (..code..)
461+
// )
462+
// )
463+
// which can be further optimized later.
464+
if (curr->name.is() && curr->list.size() == 1) {
465+
if (auto* loop = curr->list[0]->dynCast<Loop>()) {
466+
curr->list[0] = loop->body;
467+
loop->body = curr;
468+
curr->finalize(curr->type);
469+
loop->finalize();
470+
replaceCurrent(loop);
471+
worked = true;
472+
} else if (auto* iff = curr->list[0]->dynCast<If>()) {
473+
// The label can't be used in the condition.
474+
if (BranchUtils::BranchSeeker::countNamed(iff->condition, curr->name) == 0) {
475+
// We can move the block into either arm, if there are no uses in the other.
476+
Expression** target = nullptr;
477+
if (!iff->ifFalse ||
478+
BranchUtils::BranchSeeker::countNamed(iff->ifFalse, curr->name) == 0) {
479+
target = &iff->ifTrue;
480+
} else if (BranchUtils::BranchSeeker::countNamed(iff->ifTrue, curr->name) == 0) {
481+
target = &iff->ifFalse;
482+
}
483+
if (target) {
484+
curr->list[0] = *target;
485+
*target = curr;
486+
// The block used to contain the if, and may have changed type from unreachable
487+
// to none, for example, if the if has an unreachable condition but the arm
488+
// is not unreachable.
489+
curr->finalize();
490+
iff->finalize();
491+
replaceCurrent(iff);
492+
worked = true;
493+
// Note that the type might change, e.g. if the if condition is unreachable
494+
// but the block that was on the outside had a break.
495+
}
496+
}
497+
}
498+
}
499+
}
500+
} sinker;
501+
502+
sinker.doWalkFunction(func);
503+
if (sinker.worked) {
504+
anotherCycle = true;
505+
}
506+
}
507+
439508
void doWalkFunction(Function* func) {
440509
// multiple cycles may be needed
441510
bool worked = false;
@@ -463,6 +532,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
463532
anotherCycle |= optimizeLoop(loop);
464533
}
465534
loops.clear();
535+
// sink blocks
536+
sinkBlocks(func);
466537
if (anotherCycle) worked = true;
467538
} while (anotherCycle);
468539

0 commit comments

Comments
 (0)