Skip to content

Commit cd6f0d9

Browse files
authored
Simplify BinaryenIRWriter (#3110)
BinaryenIRWriter was previously inconsistent about whether or not it emitted an instruction if that instruction was not reachable. Instructions that produced values were not emitted if they were unreachable, but instructions that did not produce values were always emitted. Additionally, blocks continued to emit their children even after emitting an unreachable child. Since it was not possible to tell whether an unreachable instruction's parent would be emitted, BinaryenIRWriter had to be very defensive and emit many extra `unreachable` instructions around unreachable code to avoid type errors. This PR unifies the logic for emitting all non-control flow instructions and changes the behavior of BinaryenIRWriter so that it never emits instructions that cannot be reached due to having unreachable children. This means that extra `unreachable` instructions now only need to be emitted after unreachable control flow constructs. BinaryenIRWriter now also stops emitting instructions inside blocks after the first unreachable instruction as an extra optimization. This change will also simplify Poppy IR stackification (see #3059) by guaranteeing that instructions with unreachable children will not be emitted into the stackifier. This makes satisfying the Poppy IR rule against unreachable Pops trivial, whereas previously satisfying this rule would have required about about 700 additional lines of code to recompute the types of all unreachable children for any instruction.
1 parent 739144d commit cd6f0d9

9 files changed

+4337
-4765
lines changed

src/ir/iteration.h

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef wasm_ir_iteration_h
1818
#define wasm_ir_iteration_h
1919

20+
#include "ir/properties.h"
2021
#include "wasm-traversal.h"
2122
#include "wasm.h"
2223

@@ -29,18 +30,24 @@ namespace wasm {
2930
// * This skips missing children, e.g. if an if has no else, it is represented
3031
// as having 2 children (and not 3 with the last a nullptr).
3132
//
32-
// In general, it is preferable not to use this class and to directly access
33-
// the children (using e.g. iff->ifTrue etc.), as that is faster. However, in
34-
// cases where speed does not matter, this can be convenient.
33+
// In general, it is preferable not to use this class and to directly access the
34+
// children (using e.g. iff->ifTrue etc.), as that is faster. However, in cases
35+
// where speed does not matter, this can be convenient. TODO: reimplement these
36+
// to avoid materializing all the chilren at once.
3537
//
36-
37-
class ChildIterator {
38+
// ChildIterator - Iterates over all children
39+
//
40+
// ValueChildIterator - Iterates over all children that produce values used by
41+
// this instruction. For example, includes If::condition
42+
// but not If::ifTrue.
43+
//
44+
template<template<class, class> class Scanner> class AbstractChildIterator {
45+
using Self = AbstractChildIterator<Scanner>;
3846
struct Iterator {
39-
const ChildIterator& parent;
47+
const Self& parent;
4048
Index index;
4149

42-
Iterator(const ChildIterator& parent, Index index)
43-
: parent(parent), index(index) {}
50+
Iterator(const Self& parent, Index index) : parent(parent), index(index) {}
4451

4552
bool operator!=(const Iterator& other) const {
4653
return index != other.index || &parent != &(other.parent);
@@ -52,21 +59,21 @@ class ChildIterator {
5259
};
5360

5461
public:
55-
std::vector<Expression*> children;
62+
SmallVector<Expression*, 4> children;
5663

57-
ChildIterator(Expression* parent) {
64+
AbstractChildIterator(Expression* parent) {
5865
struct Traverser : public PostWalker<Traverser> {
5966
Expression* parent;
60-
std::vector<Expression*>* children;
67+
SmallVector<Expression*, 4>* children;
6168

6269
// We need to scan subchildren exactly once - just the parent.
6370
bool scanned = false;
6471

6572
static void scan(Traverser* self, Expression** currp) {
6673
if (!self->scanned) {
6774
self->scanned = true;
68-
PostWalker<Traverser, UnifiedExpressionVisitor<Traverser>>::scan(
69-
self, currp);
75+
Scanner<Traverser, UnifiedExpressionVisitor<Traverser>>::scan(self,
76+
currp);
7077
} else {
7178
// This is one of the children. Do not scan further, just note it.
7279
self->children->push_back(*currp);
@@ -82,6 +89,25 @@ class ChildIterator {
8289
Iterator end() const { return Iterator(*this, children.size()); }
8390
};
8491

92+
template<class SubType, class Visitor>
93+
struct ValueChildScanner : PostWalker<SubType, Visitor> {
94+
static void scan(SubType* self, Expression** currp) {
95+
auto* curr = *currp;
96+
if (Properties::isControlFlowStructure(curr)) {
97+
// If conditions are the only value children of control flow structures
98+
if (auto* iff = curr->dynCast<If>()) {
99+
self->pushTask(SubType::scan, &iff->condition);
100+
}
101+
} else {
102+
// All children on non-control flow expressions are value children
103+
PostWalker<SubType, Visitor>::scan(self, currp);
104+
}
105+
}
106+
};
107+
108+
using ChildIterator = AbstractChildIterator<PostWalker>;
109+
using ValueChildIterator = AbstractChildIterator<ValueChildScanner>;
110+
85111
// Returns true if the current expression contains a certain kind of expression,
86112
// within the given depth of BFS. If depth is -1, this searches all children.
87113
template<typename T> bool containsChild(Expression* parent, int depth = -1) {

src/ir/properties.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
#include "ir/bits.h"
2121
#include "ir/effects.h"
22-
#include "ir/iteration.h"
2322
#include "wasm.h"
2423

2524
namespace wasm {

src/ir/stack-utils.cpp

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#include "stack-utils.h"
18+
#include "ir/iteration.h"
1819
#include "ir/properties.h"
1920

2021
namespace wasm {
@@ -56,20 +57,13 @@ bool mayBeUnreachable(Expression* expr) {
5657
} // namespace StackUtils
5758

5859
StackSignature::StackSignature(Expression* expr) {
59-
params = Type::none;
60-
if (Properties::isControlFlowStructure(expr)) {
61-
if (expr->is<If>()) {
62-
params = Type::i32;
63-
}
64-
} else {
65-
std::vector<Type> inputs;
66-
for (auto* child : ChildIterator(expr)) {
67-
assert(child->type.isConcrete());
68-
// Children might be tuple pops, so expand their types
69-
inputs.insert(inputs.end(), child->type.begin(), child->type.end());
70-
}
71-
params = Type(inputs);
60+
std::vector<Type> inputs;
61+
for (auto* child : ValueChildIterator(expr)) {
62+
assert(child->type.isConcrete());
63+
// Children might be tuple pops, so expand their types
64+
inputs.insert(inputs.end(), child->type.begin(), child->type.end());
7265
}
66+
params = Type(inputs);
7367
if (expr->type == Type::unreachable) {
7468
unreachable = true;
7569
results = Type::none;

0 commit comments

Comments
 (0)