Skip to content

Commit e865f2f

Browse files
authored
Merge pull request #1095 from WebAssembly/fuzz-3
More fuzz fixes
2 parents 24accd1 + 7bc2ed7 commit e865f2f

28 files changed

+695
-83
lines changed

src/asm2wasm.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
13971397
void visitCallIndirect(CallIndirect* curr) {
13981398
// we already call into target = something + offset, where offset is a callImport with the name of the table. replace that with the table offset
13991399
// note that for an ftCall or mftCall, we have no asm.js mask, so have nothing to do here
1400-
auto* add = curr->target->dynCast<Binary>();
1400+
auto* target = curr->target;
1401+
// might be a block with a fallthrough
1402+
if (auto* block = target->dynCast<Block>()) {
1403+
target = block->list.back();
1404+
}
1405+
auto* add = target->dynCast<Binary>();
14011406
if (!add) return;
14021407
if (add->right->is<CallImport>()) {
14031408
auto* offset = add->right->cast<CallImport>();

src/ast/effects.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
3939
if (breakNames.size() > 0) branches = true;
4040
}
4141

42-
bool branches = false; // branches out of this expression
42+
bool branches = false; // branches out of this expression, returns, infinite loops, etc
4343
bool calls = false;
4444
std::set<Index> localsRead;
4545
std::set<Index> localsWritten;
@@ -138,6 +138,18 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
138138
}
139139
void visitLoop(Loop* curr) {
140140
if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
141+
// if the loop is unreachable, then there is branching control flow:
142+
// (1) if the body is unreachable because of a (return), uncaught (br) etc., then we
143+
// already noted branching, so it is ok to mark it again (if we have *caught*
144+
// (br)s, then they did not lead to the loop body being unreachable).
145+
// (same logic applies to blocks)
146+
// (2) if the loop is unreachable because it only has branches up to the loop
147+
// top, but no way to get out, then it is an infinite loop, and we consider
148+
// that a branching side effect (note how the same logic does not apply to
149+
// blocks).
150+
if (curr->type == unreachable) {
151+
branches = true;
152+
}
141153
}
142154

143155
void visitCall(Call *curr) { calls = true; }
@@ -182,6 +194,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
182194
case TruncUFloat64ToInt32:
183195
case TruncUFloat64ToInt64: {
184196
implicitTrap = true;
197+
break;
185198
}
186199
default: {}
187200
}
@@ -199,6 +212,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
199212
case RemSInt64:
200213
case RemUInt64: {
201214
implicitTrap = true;
215+
break;
202216
}
203217
default: {}
204218
}

src/ast/properties.h

+14-10
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,13 @@ struct Properties {
6060
if (auto* outer = curr->dynCast<Binary>()) {
6161
if (outer->op == ShrSInt32) {
6262
if (auto* outerConst = outer->right->dynCast<Const>()) {
63-
if (auto* inner = outer->left->dynCast<Binary>()) {
64-
if (inner->op == ShlInt32) {
65-
if (auto* innerConst = inner->right->dynCast<Const>()) {
66-
if (outerConst->value == innerConst->value) {
67-
return inner->left;
63+
if (outerConst->value.geti32() != 0) {
64+
if (auto* inner = outer->left->dynCast<Binary>()) {
65+
if (inner->op == ShlInt32) {
66+
if (auto* innerConst = inner->right->dynCast<Const>()) {
67+
if (outerConst->value == innerConst->value) {
68+
return inner->left;
69+
}
6870
}
6971
}
7072
}
@@ -87,11 +89,13 @@ struct Properties {
8789
if (auto* outer = curr->dynCast<Binary>()) {
8890
if (outer->op == ShrSInt32) {
8991
if (auto* outerConst = outer->right->dynCast<Const>()) {
90-
if (auto* inner = outer->left->dynCast<Binary>()) {
91-
if (inner->op == ShlInt32) {
92-
if (auto* innerConst = inner->right->dynCast<Const>()) {
93-
if (outerConst->value.leU(innerConst->value).geti32()) {
94-
return inner->left;
92+
if (outerConst->value.geti32() != 0) {
93+
if (auto* inner = outer->left->dynCast<Binary>()) {
94+
if (inner->op == ShlInt32) {
95+
if (auto* innerConst = inner->right->dynCast<Const>()) {
96+
if (outerConst->value.leU(innerConst->value).geti32()) {
97+
return inner->left;
98+
}
9599
}
96100
}
97101
}

src/passes/CoalesceLocals.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ struct CoalesceLocals : public WalkerPass<CFGWalker<CoalesceLocals, Visitor<Coal
196196
if (auto* get = set->value->dynCast<GetLocal>()) return get;
197197
if (auto* iff = set->value->dynCast<If>()) {
198198
if (auto* get = iff->ifTrue->dynCast<GetLocal>()) return get;
199-
if (auto* get = iff->ifFalse->dynCast<GetLocal>()) return get;
199+
if (iff->ifFalse) {
200+
if (auto* get = iff->ifFalse->dynCast<GetLocal>()) return get;
201+
}
200202
}
201203
return nullptr;
202204
}
@@ -595,11 +597,13 @@ void CoalesceLocals::pickIndices(std::vector<Index>& indices) {
595597
// Remove a copy from a set of an if, where one if arm is a get of the same set
596598
static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression*& copy, Expression*& other, Module* module) {
597599
// replace the origin with the if, and sink the set into the other non-copying arm
600+
bool tee = set->isTee();
598601
*origin = iff;
599602
set->value = other;
600603
set->finalize();
601604
other = set;
602-
if (!isConcreteWasmType(set->type)) {
605+
// if this is not a tee, then we can get rid of the copy in that arm
606+
if (!tee) {
603607
// we don't need the copy at all
604608
copy = nullptr;
605609
if (!iff->ifTrue) {

src/passes/MergeBlocks.cpp

+24-6
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,15 @@ static void optimizeBlock(Block* curr, Module* module) {
188188
}
189189
if (!child) continue;
190190
if (child->name.is()) continue; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs)
191+
if (child->type == unreachable) {
192+
// an unreachable block can have a concrete final element (which is never reached)
193+
if (!child->list.empty()) {
194+
if (isConcreteWasmType(child->list.back()->type)) {
195+
// just remove it
196+
child->list.pop_back();
197+
}
198+
}
199+
}
191200
ExpressionList merged(module->allocator);
192201
for (size_t j = 0; j < i; j++) {
193202
merged.push_back(curr->list[j]);
@@ -279,11 +288,14 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
279288
}
280289

281290
void visitSelect(Select* curr) {
291+
// TODO: for now, just stop when we see any side effect. instead, we could
292+
// check effects carefully for reordering
282293
Block* outer = nullptr;
283-
outer = optimize(curr, curr->ifTrue, outer);
284294
if (EffectAnalyzer(getPassOptions(), curr->ifTrue).hasSideEffects()) return;
285-
outer = optimize(curr, curr->ifFalse, outer);
295+
outer = optimize(curr, curr->ifTrue, outer);
286296
if (EffectAnalyzer(getPassOptions(), curr->ifFalse).hasSideEffects()) return;
297+
outer = optimize(curr, curr->ifFalse, outer);
298+
if (EffectAnalyzer(getPassOptions(), curr->condition).hasSideEffects()) return;
287299
optimize(curr, curr->condition, outer);
288300
}
289301

@@ -299,11 +311,13 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
299311
}
300312

301313
template<typename T>
302-
void handleCall(T* curr, Block* outer = nullptr) {
314+
void handleCall(T* curr) {
315+
Block* outer = nullptr;
303316
for (Index i = 0; i < curr->operands.size(); i++) {
304-
outer = optimize(curr, curr->operands[i], outer);
305317
if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return;
318+
outer = optimize(curr, curr->operands[i], outer);
306319
}
320+
return;
307321
}
308322

309323
void visitCall(Call* curr) {
@@ -315,9 +329,13 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
315329
}
316330

317331
void visitCallIndirect(CallIndirect* curr) {
318-
auto* outer = optimize(curr, curr->target);
332+
Block* outer = nullptr;
333+
for (Index i = 0; i < curr->operands.size(); i++) {
334+
if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return;
335+
outer = optimize(curr, curr->operands[i], outer);
336+
}
319337
if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) return;
320-
handleCall(curr, outer);
338+
optimize(curr, curr->target, outer);
321339
}
322340
};
323341

src/passes/OptimizeInstructions.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,12 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
517517
}
518518
}
519519
}
520+
// some operations have no effect TODO: many more
521+
if (right->value == Literal(int32_t(0))) {
522+
if (binary->op == ShlInt32 || binary->op == ShrUInt32 || binary->op == ShrSInt32) {
523+
return binary->left;
524+
}
525+
}
520526
// the square of some operations can be merged
521527
if (auto* left = binary->left->dynCast<Binary>()) {
522528
if (left->op == binary->op) {

src/passes/Vacuum.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,16 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
232232
replaceCurrent(child);
233233
return;
234234
}
235+
// if the condition is unreachable, just return it
236+
if (curr->condition->type == unreachable) {
237+
typeUpdater.noteRecursiveRemoval(curr->ifTrue);
238+
if (curr->ifFalse) {
239+
typeUpdater.noteRecursiveRemoval(curr->ifFalse);
240+
}
241+
replaceCurrent(curr->condition);
242+
return;
243+
}
244+
// from here on, we can assume the condition executed
235245
if (curr->ifFalse) {
236246
if (curr->ifFalse->is<Nop>()) {
237247
curr->ifFalse = nullptr;

src/wasm-validator.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ struct WasmValidator : public PostWalker<WasmValidator> {
220220
void shouldBeIntOrUnreachable(WasmType ty, Expression* curr, const char* text);
221221
void validateAlignment(size_t align, WasmType type, Index bytes, bool isAtomic,
222222
Expression* curr);
223-
void validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr);
223+
void validateMemBytes(uint8_t bytes, WasmType type, Expression* curr);
224224
void validateBinaryenIR(Module& wasm);
225225
};
226226

src/wasm/wasm-validator.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,17 @@ void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
261261
shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement");
262262
shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types");
263263
}
264-
void WasmValidator::validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr) {
264+
void WasmValidator::validateMemBytes(uint8_t bytes, WasmType type, Expression* curr) {
265+
if (type == unreachable) {
266+
return; // nothing to validate in this case
267+
}
265268
switch (bytes) {
266269
case 1:
267270
case 2:
268271
case 4:
269272
break;
270273
case 8: {
271-
shouldBeEqual(getWasmTypeSize(ty), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types");
274+
shouldBeEqual(getWasmTypeSize(type), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types");
272275
break;
273276
}
274277
default: fail("Memory operations must be 1,2,4, or 8 bytes", curr);

test/passes/coalesce-locals.txt

+41
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
(type $FUNCSIG$i (func (result i32)))
88
(type $FUNCSIG$vi (func (param i32)))
99
(type $7 (func (param i32) (result i32)))
10+
(type $8 (func (param f64 i32) (result i64)))
1011
(import "env" "_emscripten_autodebug_i32" (func $_emscripten_autodebug_i32 (param i32 i32) (result i32)))
1112
(import "env" "get" (func $get (result i32)))
1213
(import "env" "set" (func $set (param i32)))
@@ -1112,4 +1113,44 @@
11121113
)
11131114
(i32.const 1)
11141115
)
1116+
(func $unused-tee-with-child-if-no-else (type $4) (param $0 i32)
1117+
(loop $label$0
1118+
(drop
1119+
(if
1120+
(br $label$0)
1121+
(nop)
1122+
)
1123+
)
1124+
)
1125+
)
1126+
(func $tee_if_with_unreachable_else (type $8) (param $0 f64) (param $1 i32) (result i64)
1127+
(call $tee_if_with_unreachable_else
1128+
(if (result f64)
1129+
(get_local $1)
1130+
(get_local $0)
1131+
(tee_local $0
1132+
(unreachable)
1133+
)
1134+
)
1135+
(f64.lt
1136+
(f64.const -128)
1137+
(get_local $0)
1138+
)
1139+
)
1140+
)
1141+
(func $tee_if_with_unreachable_true (type $8) (param $0 f64) (param $1 i32) (result i64)
1142+
(call $tee_if_with_unreachable_else
1143+
(if (result f64)
1144+
(get_local $1)
1145+
(tee_local $0
1146+
(unreachable)
1147+
)
1148+
(get_local $0)
1149+
)
1150+
(f64.lt
1151+
(f64.const -128)
1152+
(get_local $0)
1153+
)
1154+
)
1155+
)
11151156
)

test/passes/coalesce-locals.wast

+42
Original file line numberDiff line numberDiff line change
@@ -1085,4 +1085,46 @@
10851085
)
10861086
(i32.const 1)
10871087
)
1088+
(func $unused-tee-with-child-if-no-else (param $0 i32)
1089+
(loop $label$0
1090+
(drop
1091+
(tee_local $0
1092+
(if
1093+
(br $label$0)
1094+
(nop)
1095+
)
1096+
)
1097+
)
1098+
)
1099+
)
1100+
(func $tee_if_with_unreachable_else (param $0 f64) (param $1 i32) (result i64)
1101+
(call $tee_if_with_unreachable_else
1102+
(tee_local $0
1103+
(if (result f64)
1104+
(get_local $1)
1105+
(get_local $0)
1106+
(unreachable)
1107+
)
1108+
)
1109+
(f64.lt
1110+
(f64.const -128)
1111+
(get_local $0)
1112+
)
1113+
)
1114+
)
1115+
(func $tee_if_with_unreachable_true (param $0 f64) (param $1 i32) (result i64)
1116+
(call $tee_if_with_unreachable_else
1117+
(tee_local $0
1118+
(if (result f64)
1119+
(get_local $1)
1120+
(unreachable)
1121+
(get_local $0)
1122+
)
1123+
)
1124+
(f64.lt
1125+
(f64.const -128)
1126+
(get_local $0)
1127+
)
1128+
)
1129+
)
10881130
)

test/passes/optimize-instructions.txt

+31
Original file line numberDiff line numberDiff line change
@@ -1962,4 +1962,35 @@
19621962
)
19631963
)
19641964
)
1965+
(func $zero-shifts-is-not-sign-ext (type $1)
1966+
(drop
1967+
(i32.eq
1968+
(i32.load16_s align=1
1969+
(i32.const 790656516)
1970+
)
1971+
(i32.const -5431187)
1972+
)
1973+
)
1974+
(drop
1975+
(i32.eq
1976+
(i32.shl
1977+
(i32.load16_s align=1
1978+
(i32.const 790656516)
1979+
)
1980+
(i32.const 1)
1981+
)
1982+
(i32.const -5431187)
1983+
)
1984+
)
1985+
)
1986+
(func $zero-ops (type $2) (result i32)
1987+
(return
1988+
(i32.eq
1989+
(i32.load16_s align=1
1990+
(i32.const 790656516)
1991+
)
1992+
(i32.const -1337)
1993+
)
1994+
)
1995+
)
19651996
)

0 commit comments

Comments
 (0)