Skip to content

Commit b93ea39

Browse files
authored
Merge pull request #1123 from WebAssembly/fuzz-2
Yet more fuzz fixes
2 parents 62f6a12 + e17202e commit b93ea39

17 files changed

Lines changed: 407 additions & 66 deletions

src/passes/MergeBlocks.cpp

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> {
154154
}
155155
};
156156

157+
static bool hasUnreachableChild(Block* block) {
158+
for (auto* test : block->list) {
159+
if (test->type == unreachable) {
160+
return true;
161+
}
162+
}
163+
return false;
164+
}
165+
157166
// core block optimizer routine
158167
static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) {
159168
bool more = true;
@@ -168,6 +177,11 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions)
168177
if (drop) {
169178
child = drop->value->dynCast<Block>();
170179
if (child) {
180+
if (hasUnreachableChild(child)) {
181+
// don't move around unreachable code, as it can change types
182+
// dce should have been run anyhow
183+
continue;
184+
}
171185
if (child->name.is()) {
172186
Expression* expression = child;
173187
// 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)
200214
}
201215
if (!child) continue;
202216
if (child->name.is()) continue; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs)
203-
if (child->type == unreachable) {
204-
// an unreachable block can have a concrete final element (which is never reached)
205-
if (!child->list.empty()) {
206-
if (isConcreteWasmType(child->list.back()->type)) {
207-
// just remove it
208-
child->list.pop_back();
209-
}
210-
}
211-
}
212217
ExpressionList merged(module->allocator);
213218
for (size_t j = 0; j < i; j++) {
214219
merged.push_back(curr->list[j]);
@@ -219,6 +224,16 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions)
219224
for (size_t j = i + 1; j < curr->list.size(); j++) {
220225
merged.push_back(curr->list[j]);
221226
}
227+
// if we merged a concrete element in the middle, drop it
228+
if (!merged.empty()) {
229+
auto* last = merged.back();
230+
for (auto*& item : merged) {
231+
if (item != last && isConcreteWasmType(item->type)) {
232+
Builder builder(*module);
233+
item = builder.makeDrop(item);
234+
}
235+
}
236+
}
222237
curr->list.swap(merged);
223238
more = true;
224239
changed = true;
@@ -241,6 +256,23 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
241256
optimizeBlock(curr, getModule(), getPassOptions());
242257
}
243258

259+
// given
260+
// (curr
261+
// (block=child
262+
// (..more..)
263+
// (back)
264+
// )
265+
// (..other..children..)
266+
// )
267+
// if child is a block, we can move this around to
268+
// (block
269+
// (..more..)
270+
// (curr
271+
// (back)
272+
// (..other..children..)
273+
// )
274+
// )
275+
// at which point the block is on the outside and potentially mergeable with an outer block
244276
Block* optimize(Expression* curr, Expression*& child, Block* outer = nullptr, Expression** dependency1 = nullptr, Expression** dependency2 = nullptr) {
245277
if (!child) return outer;
246278
if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) {
@@ -251,18 +283,29 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
251283
}
252284
if (auto* block = child->dynCast<Block>()) {
253285
if (!block->name.is() && block->list.size() >= 2) {
254-
child = block->list.back();
255-
// we modified child (which is a reference to a pointer), which modifies curr, which might change its type
256-
// (e.g. (drop (block (result i32) .. (unreachable)))
257-
// the child was a block of i32, and is being replaced with an unreachable, so the
258-
// parent will likely need to be unreachable too
259-
auto oldType = curr->type;
260-
ReFinalize().walk(curr);
286+
// if we move around unreachable code, type changes could occur. avoid that, as
287+
// anyhow it means we should have run dce before getting here
288+
if (curr->type == none && hasUnreachableChild(block)) {
289+
// moving the block to the outside would replace a none with an unreachable
290+
return outer;
291+
}
292+
auto* back = block->list.back();
293+
if (back->type == unreachable) {
294+
// curr is not reachable, dce could remove it; don't try anything fancy
295+
// here
296+
return outer;
297+
}
298+
// we are going to replace the block with the final element, so they should
299+
// be identically typed
300+
if (block->type != back->type) {
301+
return outer;
302+
}
303+
child = back;
261304
if (outer == nullptr) {
262305
// reuse the block, move it out
263306
block->list.back() = curr;
264307
// we want the block outside to have the same type as curr had
265-
block->finalize(oldType);
308+
block->finalize(curr->type);
266309
replaceCurrent(block);
267310
return block;
268311
} else {

src/passes/RemoveUnusedBrs.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
481481
// format. We need to flip the condition, which at worst adds 1.
482482
if (curr->name.is()) {
483483
auto* br = list[0]->dynCast<Break>();
484-
if (br && br->condition && br->name == curr->name) {
484+
// we seek a regular br_if; if the type is unreachable that means it is not
485+
// actually taken, so ignore
486+
if (br && br->condition && br->name == curr->name && br->type != unreachable) {
485487
assert(!br->value); // can't, it would be dropped or last in the block
486488
if (BranchUtils::BranchSeeker::countNamed(curr, curr->name) == 1) {
487489
// no other breaks to that name, so we can do this

src/passes/RemoveUnusedNames.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> {
5757
void visitBlock(Block *curr) {
5858
if (curr->name.is() && curr->list.size() == 1) {
5959
auto* child = curr->list[0]->dynCast<Block>();
60-
if (child && child->name.is()) {
60+
if (child && child->name.is() && child->type == curr->type) {
6161
// 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)
6262
auto& branches = branchesSeen[curr->name];
6363
for (auto* branch : branches) {

src/wasm-binary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@ class WasmBinaryBuilder {
861861
int depth = 0; // only for debugging
862862

863863
BinaryConsts::ASTNodes readExpression(Expression*& curr);
864+
void pushBlockElements(Block* curr, size_t start, size_t end);
864865
void visitBlock(Block *curr);
865866
Expression* getMaybeBlock(WasmType type);
866867
Expression* getBlock(WasmType type);

src/wasm/wasm-binary.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,11 @@ void WasmBinaryWriter::recursePossibleBlockContents(Expression* curr) {
583583
for (auto* child : block->list) {
584584
recurse(child);
585585
}
586+
if (block->type == unreachable && block->list.back()->type != unreachable) {
587+
// similar to in visitBlock, here we could skip emitting the block itself,
588+
// but must still end the 'block' (the contents, really) with an unreachable
589+
o << int8_t(BinaryConsts::Unreachable);
590+
}
586591
}
587592

588593
void WasmBinaryWriter::visitIf(If *curr) {
@@ -2073,6 +2078,20 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
20732078
return BinaryConsts::ASTNodes(code);
20742079
}
20752080

2081+
void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) {
2082+
for (size_t i = start; i < end; i++) {
2083+
auto* item = expressionStack[i];
2084+
curr->list.push_back(item);
2085+
if (i < end - 1) {
2086+
// stacky&unreachable code may introduce elements that need to be dropped in non-final positions
2087+
if (isConcreteWasmType(item->type)) {
2088+
curr->list.back() = Builder(wasm).makeDrop(curr->list.back());
2089+
}
2090+
}
2091+
}
2092+
expressionStack.resize(start);
2093+
}
2094+
20762095
void WasmBinaryBuilder::visitBlock(Block *curr) {
20772096
if (debug) std::cerr << "zz node: Block" << std::endl;
20782097
// special-case Block and de-recurse nested blocks in their first position, as that is
@@ -2108,18 +2127,7 @@ void WasmBinaryBuilder::visitBlock(Block *curr) {
21082127
if (end < start) {
21092128
throw ParseException("block cannot pop from outside");
21102129
}
2111-
for (size_t i = start; i < end; i++) {
2112-
if (debug) std::cerr << " " << size_t(expressionStack[i]) << "\n zz Block element " << curr->list.size() << std::endl;
2113-
auto* item = expressionStack[i];
2114-
curr->list.push_back(item);
2115-
if (i < end - 1) {
2116-
// stacky&unreachable code may introduce elements that need to be dropped in non-final positions
2117-
if (isConcreteWasmType(item->type)) {
2118-
curr->list.back() = Builder(wasm).makeDrop(curr->list.back());
2119-
}
2120-
}
2121-
}
2122-
expressionStack.resize(start);
2130+
pushBlockElements(curr, start, end);
21232131
curr->finalize(curr->type);
21242132
breakStack.pop_back();
21252133
}
@@ -2136,11 +2144,8 @@ Expression* WasmBinaryBuilder::getMaybeBlock(WasmType type) {
21362144
throw ParseException("block cannot pop from outside");
21372145
}
21382146
auto* block = allocator.alloc<Block>();
2139-
for (size_t i = start; i < end; i++) {
2140-
block->list.push_back(expressionStack[i]);
2141-
}
2147+
pushBlockElements(block, start, end);
21422148
block->finalize(type);
2143-
expressionStack.resize(start);
21442149
return block;
21452150
}
21462151

test/passes/merge-blocks.txt

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
)
1515
(func $drop-block-br (type $0)
1616
(block $block
17-
(block $x
18-
(drop
19-
(i32.const 1)
20-
)
21-
(br $x)
22-
(drop
17+
(drop
18+
(block $x (result i32)
19+
(br $x
20+
(i32.const 1)
21+
)
2322
(i32.const 0)
2423
)
2524
)
@@ -95,8 +94,8 @@
9594
(func $drop-block-squared-iloop (type $0)
9695
(drop
9796
(block $label$0 (result i32)
98-
(block $label$1
99-
(drop
97+
(drop
98+
(block $label$1
10099
(loop $label$2
101100
(br $label$2)
102101
)
@@ -124,4 +123,15 @@
124123
)
125124
)
126125
)
126+
(func $loop-block-drop-block-return (type $0)
127+
(loop $label$4
128+
(block $label$5
129+
(drop
130+
(block $label$6 (result i32)
131+
(return)
132+
)
133+
)
134+
)
135+
)
136+
)
127137
)

test/passes/merge-blocks.wast

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,16 @@
101101
)
102102
)
103103
)
104+
(func $loop-block-drop-block-return
105+
(loop $label$4
106+
(block $label$5
107+
(drop
108+
(block $label$6 (result i32)
109+
(return)
110+
)
111+
)
112+
)
113+
)
114+
)
104115
)
105116

test/passes/remove-unused-brs.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,4 +1048,15 @@
10481048
)
10491049
)
10501050
)
1051+
(func $untaken-br_if-then-if (type $1)
1052+
(block $label$0
1053+
(br_if $label$0
1054+
(unreachable)
1055+
)
1056+
(if
1057+
(i32.const 0)
1058+
(nop)
1059+
)
1060+
)
1061+
)
10511062
)

test/passes/remove-unused-brs.wast

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,5 +932,16 @@
932932
)
933933
)
934934
)
935+
(func $untaken-br_if-then-if
936+
(block $label$0
937+
(br_if $label$0
938+
(unreachable)
939+
)
940+
(if
941+
(i32.const 0)
942+
(nop)
943+
)
944+
)
945+
)
935946
)
936947

test/passes/remove-unused-names.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(module
22
(type $0 (func (param i32) (result i32)))
33
(type $1 (func))
4+
(type $2 (func (result i32)))
45
(memory $0 256 256)
56
(func $b0 (type $0) (param $i1 i32) (result i32)
67
(i32.const 0)
@@ -63,4 +64,20 @@
6364
)
6465
)
6566
)
67+
(func $merge-typed-with-unreachable-child (type $2) (result i32)
68+
(local $0 f32)
69+
(block $label$0 (result i32)
70+
(block $label$1
71+
(br_if $label$1
72+
(i32.const 0)
73+
(br_if $label$0
74+
(i32.const 0)
75+
(br $label$0
76+
(i32.const 0)
77+
)
78+
)
79+
)
80+
)
81+
)
82+
)
6683
)

0 commit comments

Comments
 (0)