From cf6c4cff644b87b4c9688931e2924e2d73b9998f Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 11 Jul 2017 14:18:52 -0700 Subject: infinite loops have side effects --- src/ast/effects.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ast/effects.h b/src/ast/effects.h index a7a5d8fb2..6e4bb617e 100644 --- a/src/ast/effects.h +++ b/src/ast/effects.h @@ -39,7 +39,7 @@ struct EffectAnalyzer : public PostWalker { if (breakNames.size() > 0) branches = true; } - bool branches = false; // branches out of this expression + bool branches = false; // branches out of this expression, returns, infinite loops, etc bool calls = false; std::set localsRead; std::set localsWritten; @@ -138,6 +138,18 @@ struct EffectAnalyzer : public PostWalker { } void visitLoop(Loop* curr) { if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks + // if the loop is unreachable, then there is branching control flow: + // (1) if the body is unreachable because of a (return), uncaught (br) etc., then we + // already noted branching, so it is ok to mark it again (if we have *caught* + // (br)s, then they did not lead to the loop body being unreachable). + // (same logic applies to blocks) + // (2) if the loop is unreachable because it only has branches up to the loop + // top, but no way to get out, then it is an infinite loop, and we consider + // that a branching side effect (note how the same logic does not apply to + // blocks). + if (curr->type == unreachable) { + branches = true; + } } void visitCall(Call *curr) { calls = true; } @@ -182,6 +194,7 @@ struct EffectAnalyzer : public PostWalker { case TruncUFloat64ToInt32: case TruncUFloat64ToInt64: { implicitTrap = true; + break; } default: {} } @@ -199,6 +212,7 @@ struct EffectAnalyzer : public PostWalker { case RemSInt64: case RemUInt64: { implicitTrap = true; + break; } default: {} } -- cgit v1.2.3 From 485166f4a184543fda936b8c458f0f4b74c0368f Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 11 Jul 2017 15:00:14 -0700 Subject: fix handling of an if in a tee without an else, in coalesce-locals --- src/passes/CoalesceLocals.cpp | 4 +++- test/passes/coalesce-locals.txt | 10 ++++++++++ test/passes/coalesce-locals.wast | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index b36542a97..87eefb85c 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -196,7 +196,9 @@ struct CoalesceLocals : public WalkerPassvalue->dynCast()) return get; if (auto* iff = set->value->dynCast()) { if (auto* get = iff->ifTrue->dynCast()) return get; - if (auto* get = iff->ifFalse->dynCast()) return get; + if (iff->ifFalse) { + if (auto* get = iff->ifFalse->dynCast()) return get; + } } return nullptr; } diff --git a/test/passes/coalesce-locals.txt b/test/passes/coalesce-locals.txt index f8dc3ef0f..ed6838ae8 100644 --- a/test/passes/coalesce-locals.txt +++ b/test/passes/coalesce-locals.txt @@ -1112,4 +1112,14 @@ ) (i32.const 1) ) + (func $unused-tee-with-child-if-no-else (type $4) (param $0 i32) + (loop $label$0 + (drop + (if + (br $label$0) + (nop) + ) + ) + ) + ) ) diff --git a/test/passes/coalesce-locals.wast b/test/passes/coalesce-locals.wast index 336c75e0e..d959cc820 100644 --- a/test/passes/coalesce-locals.wast +++ b/test/passes/coalesce-locals.wast @@ -1085,4 +1085,16 @@ ) (i32.const 1) ) + (func $unused-tee-with-child-if-no-else (param $0 i32) + (loop $label$0 + (drop + (tee_local $0 + (if + (br $label$0) + (nop) + ) + ) + ) + ) + ) ) -- cgit v1.2.3 From 26b2f331a210bf6de22816e2564516a9f59178d0 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 11 Jul 2017 18:55:13 -0700 Subject: handle an unreachable condition properly in vacuum if simplification --- src/passes/Vacuum.cpp | 6 ++++++ test/passes/vacuum.txt | 14 ++++++++++++++ test/passes/vacuum.wast | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'src') diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 40f4a6e59..0178a794d 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -232,6 +232,12 @@ struct Vacuum : public WalkerPass> { replaceCurrent(child); return; } + // if the condition is unreachable, just return it + if (curr->condition->type == unreachable) { + replaceCurrent(curr->condition); + return; + } + // from here on, we can assume the condition executed if (curr->ifFalse) { if (curr->ifFalse->is()) { curr->ifFalse = nullptr; diff --git a/test/passes/vacuum.txt b/test/passes/vacuum.txt index e07357296..7cee4edf1 100644 --- a/test/passes/vacuum.txt +++ b/test/passes/vacuum.txt @@ -273,4 +273,18 @@ ) ) ) + (func $unreachable-if-with-nop-arm-that-leaves-a-concrete-value-if-nop-is-removed (type $0) + (block $label$0 + (loop $label$1 + (br_if $label$0 + (i32.load8_s + (i32.const 1634541608) + ) + (loop $label$9 + (br $label$9) + ) + ) + ) + ) + ) ) diff --git a/test/passes/vacuum.wast b/test/passes/vacuum.wast index 77ffabd60..1885da09b 100644 --- a/test/passes/vacuum.wast +++ b/test/passes/vacuum.wast @@ -593,4 +593,22 @@ ) ) ) + (func $unreachable-if-with-nop-arm-that-leaves-a-concrete-value-if-nop-is-removed + (block $label$0 + (loop $label$1 + (if + (br_if $label$0 + (i32.load8_s + (i32.const 1634541608) + ) + (loop $label$9 + (br $label$9) + ) + ) + (nop) + (i32.const 1920103026) + ) + ) + ) + ) ) -- cgit v1.2.3 From b85e8b51464d0ea1d76d08c2a1b53648e9b7ed9c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 12 Jul 2017 11:16:04 -0700 Subject: zero shifts are not sign-extends --- src/ast/properties.h | 24 +++++++++++++---------- test/passes/optimize-instructions.txt | 30 ++++++++++++++++++++++++++++ test/passes/optimize-instructions.wast | 36 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/ast/properties.h b/src/ast/properties.h index 9121deba5..097f7a8f0 100644 --- a/src/ast/properties.h +++ b/src/ast/properties.h @@ -60,11 +60,13 @@ struct Properties { if (auto* outer = curr->dynCast()) { if (outer->op == ShrSInt32) { if (auto* outerConst = outer->right->dynCast()) { - if (auto* inner = outer->left->dynCast()) { - if (inner->op == ShlInt32) { - if (auto* innerConst = inner->right->dynCast()) { - if (outerConst->value == innerConst->value) { - return inner->left; + if (outerConst->value.geti32() != 0) { + if (auto* inner = outer->left->dynCast()) { + if (inner->op == ShlInt32) { + if (auto* innerConst = inner->right->dynCast()) { + if (outerConst->value == innerConst->value) { + return inner->left; + } } } } @@ -87,11 +89,13 @@ struct Properties { if (auto* outer = curr->dynCast()) { if (outer->op == ShrSInt32) { if (auto* outerConst = outer->right->dynCast()) { - if (auto* inner = outer->left->dynCast()) { - if (inner->op == ShlInt32) { - if (auto* innerConst = inner->right->dynCast()) { - if (outerConst->value.leU(innerConst->value).geti32()) { - return inner->left; + if (outerConst->value.geti32() != 0) { + if (auto* inner = outer->left->dynCast()) { + if (inner->op == ShlInt32) { + if (auto* innerConst = inner->right->dynCast()) { + if (outerConst->value.leU(innerConst->value).geti32()) { + return inner->left; + } } } } diff --git a/test/passes/optimize-instructions.txt b/test/passes/optimize-instructions.txt index c8fe17cf0..49c3fca8b 100644 --- a/test/passes/optimize-instructions.txt +++ b/test/passes/optimize-instructions.txt @@ -1962,4 +1962,34 @@ ) ) ) + (func $zero-shifts-is-not-sign-ext (type $1) + (drop + (i32.eq + (i32.shr_s + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const 0) + ) + (i32.const 0) + ) + (i32.const -5431187) + ) + ) + (drop + (i32.eq + (i32.shr_s + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const 1) + ) + (i32.const 0) + ) + (i32.const -5431187) + ) + ) + ) ) diff --git a/test/passes/optimize-instructions.wast b/test/passes/optimize-instructions.wast index bb89d7314..88b2bcdfe 100644 --- a/test/passes/optimize-instructions.wast +++ b/test/passes/optimize-instructions.wast @@ -2359,4 +2359,40 @@ ) ) ) + (func $zero-shifts-is-not-sign-ext + (drop + (i32.eq + (i32.const -5431187) + (i32.add + (i32.const 0) + (i32.shr_s + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const 0) + ) + (i32.const 0) + ) + ) + ) + ) + (drop + (i32.eq + (i32.const -5431187) + (i32.add + (i32.const 0) + (i32.shr_s + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const 1) + ) + (i32.const 0) + ) + ) + ) + ) + ) ) -- cgit v1.2.3 From f755250db869781db4206eee8db00c8060ac398b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 12 Jul 2017 11:37:10 -0700 Subject: optimize shifts of 0 --- src/passes/OptimizeInstructions.cpp | 6 ++++++ test/passes/optimize-instructions.txt | 31 ++++++++++++++++--------------- test/passes/optimize-instructions.wast | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index d1b419a06..c6006d712 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -517,6 +517,12 @@ struct OptimizeInstructions : public WalkerPassvalue == Literal(int32_t(0))) { + if (binary->op == ShlInt32 || binary->op == ShrUInt32 || binary->op == ShrSInt32) { + return binary->left; + } + } // the square of some operations can be merged if (auto* left = binary->left->dynCast()) { if (left->op == binary->op) { diff --git a/test/passes/optimize-instructions.txt b/test/passes/optimize-instructions.txt index 49c3fca8b..8b42fd2ad 100644 --- a/test/passes/optimize-instructions.txt +++ b/test/passes/optimize-instructions.txt @@ -1965,31 +1965,32 @@ (func $zero-shifts-is-not-sign-ext (type $1) (drop (i32.eq - (i32.shr_s - (i32.shl - (i32.load16_s align=1 - (i32.const 790656516) - ) - (i32.const 0) - ) - (i32.const 0) + (i32.load16_s align=1 + (i32.const 790656516) ) (i32.const -5431187) ) ) (drop (i32.eq - (i32.shr_s - (i32.shl - (i32.load16_s align=1 - (i32.const 790656516) - ) - (i32.const 1) + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) ) - (i32.const 0) + (i32.const 1) ) (i32.const -5431187) ) ) ) + (func $zero-ops (type $2) (result i32) + (return + (i32.eq + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const -1337) + ) + ) + ) ) diff --git a/test/passes/optimize-instructions.wast b/test/passes/optimize-instructions.wast index 88b2bcdfe..6d1fd60ed 100644 --- a/test/passes/optimize-instructions.wast +++ b/test/passes/optimize-instructions.wast @@ -2395,4 +2395,26 @@ ) ) ) + (func $zero-ops (result i32) + (return + (i32.eq + (i32.const -1337) + (i32.shr_u + (i32.add + (i32.const 0) + (i32.shr_s + (i32.shl + (i32.load16_s align=1 + (i32.const 790656516) + ) + (i32.const 0) + ) + (i32.const 0) + ) + ) + (i32.const 0) + ) + ) + ) + ) ) -- cgit v1.2.3 From 29cf8735b0a0dd1cd6640e27206f2e2ac78503c1 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 13 Jul 2017 10:31:28 -0700 Subject: note changes when removing an if body in vacuum --- src/passes/Vacuum.cpp | 4 ++++ test/passes/vacuum.txt | 14 ++++++++++++++ test/passes/vacuum.wast | 26 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'src') diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 0178a794d..3f27fb36a 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -234,6 +234,10 @@ struct Vacuum : public WalkerPass> { } // if the condition is unreachable, just return it if (curr->condition->type == unreachable) { + typeUpdater.noteRecursiveRemoval(curr->ifTrue); + if (curr->ifFalse) { + typeUpdater.noteRecursiveRemoval(curr->ifFalse); + } replaceCurrent(curr->condition); return; } diff --git a/test/passes/vacuum.txt b/test/passes/vacuum.txt index 7cee4edf1..f3b06af1a 100644 --- a/test/passes/vacuum.txt +++ b/test/passes/vacuum.txt @@ -287,4 +287,18 @@ ) ) ) + (func $if-arm-vanishes (type $3) (result i32) + (block $label$0 (result i32) + (br $label$0 + (i32.const 1) + ) + ) + ) + (func $if-arm-vanishes-2 (type $3) (result i32) + (block $label$0 (result i32) + (br $label$0 + (i32.const 1) + ) + ) + ) ) diff --git a/test/passes/vacuum.wast b/test/passes/vacuum.wast index 1885da09b..db4bf6749 100644 --- a/test/passes/vacuum.wast +++ b/test/passes/vacuum.wast @@ -611,4 +611,30 @@ ) ) ) + (func $if-arm-vanishes (result i32) + (block $label$0 (result i32) + (block $label$1 + (if + (br $label$0 + (i32.const 1) + ) + (br $label$1) + ) + ) + (i32.const 1579493952) + ) + ) + (func $if-arm-vanishes-2 (result i32) + (block $label$0 (result i32) + (block $label$1 + (if + (br $label$0 + (i32.const 1) + ) + (br $label$1) + ) + ) + (i32.const 1579493952) + ) + ) ) -- cgit v1.2.3 From 00dd3b97455652113fa36cf639315388052f502d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 13 Jul 2017 12:00:11 -0700 Subject: fix validation of memBytes, if the load type is unreachable, we can't and shouldn't try to validate --- src/wasm-validator.h | 2 +- src/wasm/wasm-validator.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 2ec530476..f83f5209d 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -217,7 +217,7 @@ public: void validateAlignment(size_t align, WasmType type, Index bytes, bool isAtomic, Expression* curr); - void validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr); + void validateMemBytes(uint8_t bytes, WasmType type, Expression* curr); void validateBinaryenIR(Module& wasm); }; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index c1f44a68b..25f2588d3 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -237,14 +237,17 @@ void WasmValidator::visitAtomicRMW(AtomicRMW* curr) { void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { validateMemBytes(curr->bytes, curr->type, curr); } -void WasmValidator::validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr) { +void WasmValidator::validateMemBytes(uint8_t bytes, WasmType type, Expression* curr) { + if (type == unreachable) { + return; // nothing to validate in this case + } switch (bytes) { case 1: case 2: case 4: break; case 8: { - shouldBeEqual(getWasmTypeSize(ty), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types"); + shouldBeEqual(getWasmTypeSize(type), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types"); break; } default: fail("Memory operations must be 1,2,4, or 8 bytes", curr); -- cgit v1.2.3 From 9792a40a8704cbae0ee515126c0b63eb9879d626 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 13 Jul 2017 17:55:10 -0700 Subject: when removing an if-copy in coalesce-locals, if it's a tee, we do still need the get in that arm. only when it is not a tee can we remove that arm and make the if-else into an if --- src/passes/CoalesceLocals.cpp | 4 +++- test/passes/coalesce-locals.txt | 31 +++++++++++++++++++++++++++++++ test/passes/coalesce-locals.wast | 30 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index 87eefb85c..5ab68da23 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -597,11 +597,13 @@ void CoalesceLocals::pickIndices(std::vector& indices) { // Remove a copy from a set of an if, where one if arm is a get of the same set static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression*& copy, Expression*& other, Module* module) { // replace the origin with the if, and sink the set into the other non-copying arm + bool tee = set->isTee(); *origin = iff; set->value = other; set->finalize(); other = set; - if (!isConcreteWasmType(set->type)) { + // if this is not a tee, then we can get rid of the copy in that arm + if (!tee) { // we don't need the copy at all copy = nullptr; if (!iff->ifTrue) { diff --git a/test/passes/coalesce-locals.txt b/test/passes/coalesce-locals.txt index ed6838ae8..32da026ac 100644 --- a/test/passes/coalesce-locals.txt +++ b/test/passes/coalesce-locals.txt @@ -7,6 +7,7 @@ (type $FUNCSIG$i (func (result i32))) (type $FUNCSIG$vi (func (param i32))) (type $7 (func (param i32) (result i32))) + (type $8 (func (param f64 i32) (result i64))) (import "env" "_emscripten_autodebug_i32" (func $_emscripten_autodebug_i32 (param i32 i32) (result i32))) (import "env" "get" (func $get (result i32))) (import "env" "set" (func $set (param i32))) @@ -1122,4 +1123,34 @@ ) ) ) + (func $tee_if_with_unreachable_else (type $8) (param $0 f64) (param $1 i32) (result i64) + (call $tee_if_with_unreachable_else + (if (result f64) + (get_local $1) + (get_local $0) + (tee_local $0 + (unreachable) + ) + ) + (f64.lt + (f64.const -128) + (get_local $0) + ) + ) + ) + (func $tee_if_with_unreachable_true (type $8) (param $0 f64) (param $1 i32) (result i64) + (call $tee_if_with_unreachable_else + (if (result f64) + (get_local $1) + (tee_local $0 + (unreachable) + ) + (get_local $0) + ) + (f64.lt + (f64.const -128) + (get_local $0) + ) + ) + ) ) diff --git a/test/passes/coalesce-locals.wast b/test/passes/coalesce-locals.wast index d959cc820..ee92bb05e 100644 --- a/test/passes/coalesce-locals.wast +++ b/test/passes/coalesce-locals.wast @@ -1097,4 +1097,34 @@ ) ) ) + (func $tee_if_with_unreachable_else (param $0 f64) (param $1 i32) (result i64) + (call $tee_if_with_unreachable_else + (tee_local $0 + (if (result f64) + (get_local $1) + (get_local $0) + (unreachable) + ) + ) + (f64.lt + (f64.const -128) + (get_local $0) + ) + ) + ) + (func $tee_if_with_unreachable_true (param $0 f64) (param $1 i32) (result i64) + (call $tee_if_with_unreachable_else + (tee_local $0 + (if (result f64) + (get_local $1) + (unreachable) + (get_local $0) + ) + ) + (f64.lt + (f64.const -128) + (get_local $0) + ) + ) + ) ) -- cgit v1.2.3 From 908a74b7db6c51e6ca450a3f68a5ffe4c011f316 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Fri, 14 Jul 2017 16:27:17 -0700 Subject: handle an unreachable block with a reachable final element in merge-blocks --- src/passes/MergeBlocks.cpp | 9 +++++++++ test/passes/remove-unused-names_merge-blocks.txt | 5 +++++ test/passes/remove-unused-names_merge-blocks.wast | 9 +++++++++ 3 files changed, 23 insertions(+) (limited to 'src') diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 2c983d991..bce5798a5 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -188,6 +188,15 @@ static void optimizeBlock(Block* curr, Module* module) { } 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]); diff --git a/test/passes/remove-unused-names_merge-blocks.txt b/test/passes/remove-unused-names_merge-blocks.txt index ed448607d..d9186a46e 100644 --- a/test/passes/remove-unused-names_merge-blocks.txt +++ b/test/passes/remove-unused-names_merge-blocks.txt @@ -4,6 +4,7 @@ (type $iii (func (param i32 i32 i32))) (type $3 (func)) (type $4 (func (result i32))) + (type $5 (func (result f64))) (table 1 1 anyfunc) (elem (i32.const 0) $call-i) (memory $0 256 256) @@ -748,4 +749,8 @@ ) (unreachable) ) + (func $concrete_finale_in_unreachable (type $5) (result f64) + (unreachable) + (f64.const -1) + ) ) diff --git a/test/passes/remove-unused-names_merge-blocks.wast b/test/passes/remove-unused-names_merge-blocks.wast index 5cefa357e..f301bbc2e 100644 --- a/test/passes/remove-unused-names_merge-blocks.wast +++ b/test/passes/remove-unused-names_merge-blocks.wast @@ -926,4 +926,13 @@ (unreachable) ) ) + (func $concrete_finale_in_unreachable (result f64) + (block $label$0 (result f64) + (block ;; this block is unreachable + (unreachable) + (f64.const 6.322092475576799e-96) + ) + (f64.const -1) + ) + ) ) -- cgit v1.2.3 From 7bc2ed70de137aa6615fcd5d0e1f3e88f008a738 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Fri, 14 Jul 2017 21:39:26 -0700 Subject: fix merge-blocks logic in call, call_indirect, select, we need to avoid any danger of moving something past a side effect ; also fix an asm2wasm bug with call_indirect fixups; the call target may be a block, which we need to look through --- src/asm2wasm.h | 7 +- src/passes/MergeBlocks.cpp | 21 ++- test/passes/remove-unused-names_merge-blocks.txt | 174 +++++++++++++++------- test/passes/remove-unused-names_merge-blocks.wast | 44 ++++++ test/unit.asm.js | 7 + test/unit.fromasm | 19 +++ test/unit.fromasm.clamp | 19 +++ test/unit.fromasm.clamp.no-opts | 19 +++ test/unit.fromasm.imprecise | 19 +++ test/unit.fromasm.imprecise.no-opts | 19 +++ test/unit.fromasm.no-opts | 19 +++ 11 files changed, 307 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 2245bd2b5..e2b1802b5 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1397,7 +1397,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) { void visitCallIndirect(CallIndirect* curr) { // we already call into target = something + offset, where offset is a callImport with the name of the table. replace that with the table offset // note that for an ftCall or mftCall, we have no asm.js mask, so have nothing to do here - auto* add = curr->target->dynCast(); + auto* target = curr->target; + // might be a block with a fallthrough + if (auto* block = target->dynCast()) { + target = block->list.back(); + } + auto* add = target->dynCast(); if (!add) return; if (add->right->is()) { auto* offset = add->right->cast(); diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index bce5798a5..bc5fea6fb 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -288,11 +288,14 @@ struct MergeBlocks : public WalkerPass> { } void visitSelect(Select* curr) { + // TODO: for now, just stop when we see any side effect. instead, we could + // check effects carefully for reordering Block* outer = nullptr; - outer = optimize(curr, curr->ifTrue, outer); if (EffectAnalyzer(getPassOptions(), curr->ifTrue).hasSideEffects()) return; - outer = optimize(curr, curr->ifFalse, outer); + outer = optimize(curr, curr->ifTrue, outer); if (EffectAnalyzer(getPassOptions(), curr->ifFalse).hasSideEffects()) return; + outer = optimize(curr, curr->ifFalse, outer); + if (EffectAnalyzer(getPassOptions(), curr->condition).hasSideEffects()) return; optimize(curr, curr->condition, outer); } @@ -308,11 +311,13 @@ struct MergeBlocks : public WalkerPass> { } template - void handleCall(T* curr, Block* outer = nullptr) { + void handleCall(T* curr) { + Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { - outer = optimize(curr, curr->operands[i], outer); if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return; + outer = optimize(curr, curr->operands[i], outer); } + return; } void visitCall(Call* curr) { @@ -324,9 +329,13 @@ struct MergeBlocks : public WalkerPass> { } void visitCallIndirect(CallIndirect* curr) { - auto* outer = optimize(curr, curr->target); + Block* outer = nullptr; + for (Index i = 0; i < curr->operands.size(); i++) { + if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return; + outer = optimize(curr, curr->operands[i], outer); + } if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) return; - handleCall(curr, outer); + optimize(curr, curr->target, outer); } }; diff --git a/test/passes/remove-unused-names_merge-blocks.txt b/test/passes/remove-unused-names_merge-blocks.txt index d9186a46e..bc42f2766 100644 --- a/test/passes/remove-unused-names_merge-blocks.txt +++ b/test/passes/remove-unused-names_merge-blocks.txt @@ -412,26 +412,34 @@ ) ) ) - (unreachable) - (drop - (i32.const 30) - ) - (drop - (i32.const 50) - ) (drop (select - (i32.const 20) - (i32.const 40) - (i32.const 60) + (block (result i32) + (unreachable) + (i32.const 20) + ) + (block (result i32) + (drop + (i32.const 30) + ) + (i32.const 40) + ) + (block (result i32) + (drop + (i32.const 50) + ) + (i32.const 60) + ) ) ) - (drop - (i32.const 10) - ) (drop (select - (unreachable) + (block (result i32) + (drop + (i32.const 10) + ) + (unreachable) + ) (block (result i32) (drop (i32.const 30) @@ -449,27 +457,33 @@ (drop (i32.const 10) ) - (unreachable) - (drop - (i32.const 50) - ) (drop (select (i32.const 20) - (i32.const 40) - (i32.const 60) + (block + (unreachable) + (i32.const 40) + ) + (block (result i32) + (drop + (i32.const 50) + ) + (i32.const 60) + ) ) ) (drop (i32.const 10) ) - (drop - (i32.const 30) - ) (drop (select (i32.const 20) - (unreachable) + (block + (drop + (i32.const 30) + ) + (unreachable) + ) (block (result i32) (drop (i32.const 50) @@ -484,12 +498,14 @@ (drop (i32.const 30) ) - (unreachable) (drop (select (i32.const 20) (i32.const 40) - (i32.const 60) + (block + (unreachable) + (i32.const 60) + ) ) ) (drop @@ -498,14 +514,16 @@ (drop (i32.const 30) ) - (drop - (i32.const 50) - ) (drop (select (i32.const 20) (i32.const 40) - (unreachable) + (block + (drop + (i32.const 50) + ) + (unreachable) + ) ) ) ) @@ -590,19 +608,25 @@ (i32.const 20) (i32.const 40) ) - (unreachable) - (drop - (i32.const 20) - ) (call $call-ii - (i32.const 10) - (i32.const 30) - ) - (drop - (i32.const 10) + (block (result i32) + (unreachable) + (i32.const 10) + ) + (block (result i32) + (drop + (i32.const 20) + ) + (i32.const 30) + ) ) (call $call-ii - (unreachable) + (block (result i32) + (drop + (i32.const 10) + ) + (unreachable) + ) (block (result i32) (drop (i32.const 20) @@ -613,20 +637,24 @@ (drop (i32.const 10) ) - (unreachable) (call $call-ii (i32.const 20) - (i32.const 30) + (block + (unreachable) + (i32.const 30) + ) ) (drop (i32.const 10) ) - (drop - (i32.const 30) - ) (call $call-ii (i32.const 20) - (unreachable) + (block + (drop + (i32.const 30) + ) + (unreachable) + ) ) (drop (i32.const 10) @@ -653,23 +681,20 @@ (i32.const 30) (i32.const 50) ) - (drop - (i32.const 50) - ) (drop (i32.const 10) ) (drop (i32.const 30) ) + (drop + (i32.const 50) + ) (call_indirect $ii (i32.const 20) (i32.const 40) (i32.const 60) ) - (drop - (i32.const 50) - ) (call_indirect $ii (unreachable) (block (result i32) @@ -678,7 +703,50 @@ ) (i32.const 40) ) - (i32.const 60) + (block (result i32) + (drop + (i32.const 50) + ) + (i32.const 60) + ) + ) + (drop + (i32.const 31) + ) + (call_indirect $ii + (i32.const 41) + (unreachable) + (block (result i32) + (drop + (i32.const 51) + ) + (i32.const 61) + ) + ) + (drop + (i32.const 32) + ) + (drop + (i32.const 52) + ) + (call_indirect $ii + (i32.const 42) + (i32.const 62) + (unreachable) + ) + ) + (func $mix-select (type $i) (param $x i32) + (drop + (select + (get_local $x) + (get_local $x) + (block (result i32) + (set_local $x + (i32.const 1) + ) + (i32.const 2) + ) + ) ) ) (func $block-type-change (type $3) diff --git a/test/passes/remove-unused-names_merge-blocks.wast b/test/passes/remove-unused-names_merge-blocks.wast index f301bbc2e..346dc78f8 100644 --- a/test/passes/remove-unused-names_merge-blocks.wast +++ b/test/passes/remove-unused-names_merge-blocks.wast @@ -854,6 +854,50 @@ (i32.const 60) ) ) + (call_indirect $ii + (block $block21 (result i32) + (drop + (i32.const 31) + ) + (i32.const 41) + ) + (unreachable) + (block $block22 (result i32) + (drop + (i32.const 51) + ) + (i32.const 61) + ) + ) + (call_indirect $ii + (block $block21 (result i32) + (drop + (i32.const 32) + ) + (i32.const 42) + ) + (block $block22 (result i32) + (drop + (i32.const 52) + ) + (i32.const 62) + ) + (unreachable) + ) + ) + (func $mix-select (param $x i32) + (drop + (select + (get_local $x) + (get_local $x) + (block (result i32) + (set_local $x ;; cannot be moved before the gets + (i32.const 1) + ) + (i32.const 2) + ) + ) + ) ) (func $block-type-change (type $3) (local $0 f64) diff --git a/test/unit.asm.js b/test/unit.asm.js index 0bd0ab21b..22e180078 100644 --- a/test/unit.asm.js +++ b/test/unit.asm.js @@ -709,12 +709,19 @@ function asm(global, env, buffer) { i1 = (FUNCTION_TABLE_vi[1 & 7](0), 1); } + function emterpretify_assertions_safeHeap() { + var i1 = 0; + // assignment into the function table param, optimizer can do things there + FUNCTION_TABLE_vi[(Int = 1) & 7](i1 | 0); + } + function keepAlive() { sqrts(3.14159); f2u(100.0); f2s(100.0); autoDrop(52) | 0; indirectInSequence(); + emterpretify_assertions_safeHeap(); } function v() { diff --git a/test/unit.fromasm b/test/unit.fromasm index c869e2398..68f5920ee 100644 --- a/test/unit.fromasm +++ b/test/unit.fromasm @@ -1169,6 +1169,24 @@ (i32.const 17) ) ) + (func $emterpretify_assertions_safeHeap + (local $0 i32) + (call_indirect $FUNCSIG$vi + (get_local $0) + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (i32.add + (i32.and + (get_global $Int) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1191,6 +1209,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $vi (param $0 i32) (nop) diff --git a/test/unit.fromasm.clamp b/test/unit.fromasm.clamp index 370e66c4d..1442ea947 100644 --- a/test/unit.fromasm.clamp +++ b/test/unit.fromasm.clamp @@ -1193,6 +1193,24 @@ (i32.const 17) ) ) + (func $emterpretify_assertions_safeHeap + (local $0 i32) + (call_indirect $FUNCSIG$vi + (get_local $0) + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (i32.add + (i32.and + (get_global $Int) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1215,6 +1233,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $vi (param $0 i32) (nop) diff --git a/test/unit.fromasm.clamp.no-opts b/test/unit.fromasm.clamp.no-opts index 8ff26568e..37456128f 100644 --- a/test/unit.fromasm.clamp.no-opts +++ b/test/unit.fromasm.clamp.no-opts @@ -1977,6 +1977,24 @@ ) ) ) + (func $emterpretify_assertions_safeHeap + (local $i1 i32) + (call_indirect $FUNCSIG$vi + (get_local $i1) + (i32.add + (i32.and + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (get_global $Int) + ) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1999,6 +2017,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $v (nop) diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise index 45912cffb..ea664d5f0 100644 --- a/test/unit.fromasm.imprecise +++ b/test/unit.fromasm.imprecise @@ -1142,6 +1142,24 @@ (i32.const 17) ) ) + (func $emterpretify_assertions_safeHeap + (local $0 i32) + (call_indirect $FUNCSIG$vi + (get_local $0) + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (i32.add + (i32.and + (get_global $Int) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1164,6 +1182,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $vi (param $0 i32) (nop) diff --git a/test/unit.fromasm.imprecise.no-opts b/test/unit.fromasm.imprecise.no-opts index 3747f2c90..227cf5e79 100644 --- a/test/unit.fromasm.imprecise.no-opts +++ b/test/unit.fromasm.imprecise.no-opts @@ -1937,6 +1937,24 @@ ) ) ) + (func $emterpretify_assertions_safeHeap + (local $i1 i32) + (call_indirect $FUNCSIG$vi + (get_local $i1) + (i32.add + (i32.and + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (get_global $Int) + ) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1959,6 +1977,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $v (nop) diff --git a/test/unit.fromasm.no-opts b/test/unit.fromasm.no-opts index dba91cfcd..878011cef 100644 --- a/test/unit.fromasm.no-opts +++ b/test/unit.fromasm.no-opts @@ -1953,6 +1953,24 @@ ) ) ) + (func $emterpretify_assertions_safeHeap + (local $i1 i32) + (call_indirect $FUNCSIG$vi + (get_local $i1) + (i32.add + (i32.and + (block (result i32) + (set_global $Int + (i32.const 1) + ) + (get_global $Int) + ) + (i32.const 7) + ) + (i32.const 16) + ) + ) + ) (func $keepAlive (drop (call $sqrts @@ -1975,6 +1993,7 @@ ) ) (call $indirectInSequence) + (call $emterpretify_assertions_safeHeap) ) (func $v (nop) -- cgit v1.2.3