diff options
-rwxr-xr-x | auto_update_tests.py | 2 | ||||
-rwxr-xr-x | check.py | 2 | ||||
-rw-r--r-- | src/ast/bits.h | 20 | ||||
-rw-r--r-- | src/ast/literal-utils.h | 26 | ||||
-rw-r--r-- | src/passes/MergeBlocks.cpp | 34 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 46 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 13 | ||||
-rw-r--r-- | test/passes/fuzz-exec_O.txt | 25 | ||||
-rw-r--r-- | test/passes/fuzz-exec_O.wast | 23 | ||||
-rw-r--r-- | test/passes/merge-blocks.txt | 32 | ||||
-rw-r--r-- | test/passes/merge-blocks.wast | 19 | ||||
-rw-r--r-- | test/passes/optimize-instructions.txt | 73 | ||||
-rw-r--r-- | test/passes/optimize-instructions.wast | 65 | ||||
-rw-r--r-- | test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt | 70 | ||||
-rw-r--r-- | test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast | 64 | ||||
-rw-r--r-- | test/untaken-br_if.wast | 14 | ||||
-rw-r--r-- | test/untaken-br_if.wast.from-wast | 16 | ||||
-rw-r--r-- | test/untaken-br_if.wast.fromBinary | 27 | ||||
-rw-r--r-- | test/untaken-br_if.wast.fromBinary.noDebugInfo | 27 |
19 files changed, 546 insertions, 52 deletions
diff --git a/auto_update_tests.py b/auto_update_tests.py index 7e07af3a5..7aab8ea4e 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -93,7 +93,7 @@ for t in sorted(os.listdir(os.path.join('test', 'passes'))): print '..', t binary = '.wasm' in t passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '') - opts = ['-' + passname] if passname.startswith('O') else ['--' + p for p in passname.split('_')] + opts = [('--' + p if not p.startswith('O') else '-' + p) for p in passname.split('_')] t = os.path.join('test', 'passes', t) actual = '' for module, asserts in split_wast(t): @@ -78,7 +78,7 @@ for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'passes'))): print '..', t binary = '.wasm' in t passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '') - opts = ['-' + passname] if passname.startswith('O') else ['--' + p for p in passname.split('_')] + opts = [('--' + p if not p.startswith('O') else '-' + p) for p in passname.split('_')] t = os.path.join(options.binaryen_test, 'passes', t) actual = '' for module, asserts in split_wast(t): diff --git a/src/ast/bits.h b/src/ast/bits.h index d88cb5edb..11cf7b06d 100644 --- a/src/ast/bits.h +++ b/src/ast/bits.h @@ -39,6 +39,26 @@ struct Bits { // this is indeed a mask return 32 - CountLeadingZeroes(mask); } + + // gets the number of effective shifts a shift operation does. In + // wasm, only 5 bits matter for 32-bit shifts, and 6 for 64. + static Index getEffectiveShifts(Index amount, WasmType type) { + if (type == i32) { + return amount & 31; + } else if (type == i64) { + return amount & 63; + } + WASM_UNREACHABLE(); + } + + static Index getEffectiveShifts(Const* amount) { + if (amount->type == i32) { + return getEffectiveShifts(amount->value.geti32(), i32); + } else if (amount->type == i64) { + return getEffectiveShifts(amount->value.geti64(), i64); + } + WASM_UNREACHABLE(); + } }; } // namespace wasm diff --git a/src/ast/literal-utils.h b/src/ast/literal-utils.h index 7e75e8bc8..afa8146b9 100644 --- a/src/ast/literal-utils.h +++ b/src/ast/literal-utils.h @@ -23,21 +23,31 @@ namespace wasm { namespace LiteralUtils { -inline Expression* makeZero(WasmType type, Module& wasm) { - Literal value; +inline Literal makeLiteralFromInt32(int32_t x, WasmType type) { switch (type) { - case i32: value = Literal(int32_t(0)); break; - case i64: value = Literal(int64_t(0)); break; - case f32: value = Literal(float(0)); break; - case f64: value = Literal(double(0)); break; + case i32: return Literal(int32_t(x)); break; + case i64: return Literal(int64_t(x)); break; + case f32: return Literal(float(x)); break; + case f64: return Literal(double(x)); break; default: WASM_UNREACHABLE(); } +} + +inline Literal makeLiteralZero(WasmType type) { + return makeLiteralFromInt32(0, type); +} + +inline Expression* makeFromInt32(int32_t x, WasmType type, Module& wasm) { auto* ret = wasm.allocator.alloc<Const>(); - ret->value = value; - ret->type = value.type; + ret->value = makeLiteralFromInt32(x, type); + ret->type = type; return ret; } +inline Expression* makeZero(WasmType type, Module& wasm) { + return makeFromInt32(0, type, wasm); +} + } // namespace LiteralUtils } // namespace wasm diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index d32948bee..7c086a920 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -73,14 +73,23 @@ namespace wasm { // For example, if there is a switch targeting us, we can't do it - we can't remove the value from other targets struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { Name origin; - bool foundSwitch = false; + bool foundProblem = false; // count br_ifs, and dropped br_ifs. if they don't match, then a br_if flow value is used, and we can't drop it Index brIfs = 0; Index droppedBrIfs = 0; + PassOptions& passOptions; + + ProblemFinder(PassOptions& passOptions) : passOptions(passOptions) {} void visitBreak(Break* curr) { - if (curr->name == origin && curr->condition) { - brIfs++; + if (curr->name == origin) { + if (curr->condition) { + brIfs++; + } + // if the value has side effects, we can't remove it + if (EffectAnalyzer(passOptions, curr->value).hasSideEffects()) { + foundProblem = true; + } } } @@ -94,12 +103,12 @@ struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { void visitSwitch(Switch* curr) { if (curr->default_ == origin) { - foundSwitch = true; + foundProblem = true; return; } for (auto& target : curr->targets) { if (target == origin) { - foundSwitch = true; + foundProblem = true; return; } } @@ -107,7 +116,7 @@ struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { bool found() { assert(brIfs >= droppedBrIfs); - return foundSwitch || brIfs > droppedBrIfs; + return foundProblem || brIfs > droppedBrIfs; } }; @@ -115,6 +124,9 @@ struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { // While doing so it can create new blocks, so optimize blocks as well. struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> { Name origin; + PassOptions& passOptions; + + BreakValueDropper(PassOptions& passOptions) : passOptions(passOptions) {} void visitBlock(Block* curr); @@ -143,7 +155,7 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> { }; // core block optimizer routine -static void optimizeBlock(Block* curr, Module* module) { +static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) { bool more = true; bool changed = false; while (more) { @@ -159,14 +171,14 @@ static void optimizeBlock(Block* curr, Module* module) { if (child->name.is()) { Expression* expression = child; // check if it's ok to remove the value from all breaks to us - ProblemFinder finder; + ProblemFinder finder(passOptions); finder.origin = child->name; finder.walk(expression); if (finder.found()) { child = nullptr; } else { // fix up breaks - BreakValueDropper fixer; + BreakValueDropper fixer(passOptions); fixer.origin = child->name; fixer.setModule(module); fixer.walk(expression); @@ -217,7 +229,7 @@ static void optimizeBlock(Block* curr, Module* module) { } void BreakValueDropper::visitBlock(Block* curr) { - optimizeBlock(curr, getModule()); + optimizeBlock(curr, getModule(), passOptions); } struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { @@ -226,7 +238,7 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { Pass* create() override { return new MergeBlocks; } void visitBlock(Block *curr) { - optimizeBlock(curr, getModule()); + optimizeBlock(curr, getModule(), getPassOptions()); } Block* optimize(Expression* curr, Expression*& child, Block* outer = nullptr, Expression** dependency1 = nullptr, Expression** dependency2 = nullptr) { diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index de6f96ccc..4fd7cbe8d 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -29,6 +29,7 @@ #include <ast/effects.h> #include <ast/manipulation.h> #include <ast/properties.h> +#include <ast/literal-utils.h> namespace wasm { @@ -188,14 +189,14 @@ Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) { case OrInt32: case XorInt32: return std::max(getMaxBits(binary->left, localInfoProvider), getMaxBits(binary->right, localInfoProvider)); case ShlInt32: { if (auto* shifts = binary->right->dynCast<Const>()) { - return std::min(Index(32), getMaxBits(binary->left, localInfoProvider) + shifts->value.geti32()); + return std::min(Index(32), getMaxBits(binary->left, localInfoProvider) + Bits::getEffectiveShifts(shifts)); } return 32; } case ShrUInt32: { if (auto* shift = binary->right->dynCast<Const>()) { auto maxBits = getMaxBits(binary->left, localInfoProvider); - auto shifts = std::min(Index(shift->value.geti32()), maxBits); // can ignore more shifts than zero us out + auto shifts = std::min(Index(Bits::getEffectiveShifts(shift)), maxBits); // can ignore more shifts than zero us out return std::max(Index(0), maxBits - shifts); } return 32; @@ -204,7 +205,7 @@ Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) { if (auto* shift = binary->right->dynCast<Const>()) { auto maxBits = getMaxBits(binary->left, localInfoProvider); if (maxBits == 32) return 32; - auto shifts = std::min(Index(shift->value.geti32()), maxBits); // can ignore more shifts than zero us out + auto shifts = std::min(Index(Bits::getEffectiveShifts(shift)), maxBits); // can ignore more shifts than zero us out return std::max(Index(0), maxBits - shifts); } return 32; @@ -533,9 +534,16 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } else if (left->op == OrInt32) { leftRight->value = leftRight->value.or_(right->value); return left; - } else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32) { - leftRight->value = leftRight->value.add(right->value); - return left; + } else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32 || + left->op == ShlInt64 || left->op == ShrUInt64 || left->op == ShrSInt64) { + // shifts only use an effective amount from the constant, so adding must + // be done carefully + auto total = Bits::getEffectiveShifts(leftRight) + Bits::getEffectiveShifts(right); + if (total == Bits::getEffectiveShifts(total, right->type)) { + // no overflow, we can do this + leftRight->value = LiteralUtils::makeLiteralFromInt32(total, right->type); + return left; + } // TODO: handle overflows } } } @@ -874,14 +882,17 @@ private: auto* left = binary->left; auto* right = binary->right; if (!Properties::emitsBoolean(left) || !Properties::emitsBoolean(right)) return nullptr; - auto leftEffects = EffectAnalyzer(getPassOptions(), left).hasSideEffects(); - auto rightEffects = EffectAnalyzer(getPassOptions(), right).hasSideEffects(); - if (leftEffects && rightEffects) return nullptr; // both must execute - // canonicalize with side effects, if any, happening on the left - if (rightEffects) { + auto leftEffects = EffectAnalyzer(getPassOptions(), left); + auto rightEffects = EffectAnalyzer(getPassOptions(), right); + auto leftHasSideEffects = leftEffects.hasSideEffects(); + auto rightHasSideEffects = rightEffects.hasSideEffects(); + if (leftHasSideEffects && rightHasSideEffects) return nullptr; // both must execute + // canonicalize with side effects, if any, happening on the left + if (rightHasSideEffects) { if (CostAnalyzer(left).cost < MIN_COST) return nullptr; // avoidable code is too cheap + if (leftEffects.invalidates(rightEffects)) return nullptr; // cannot reorder std::swap(left, right); - } else if (leftEffects) { + } else if (leftHasSideEffects) { if (CostAnalyzer(right).cost < MIN_COST) return nullptr; // avoidable code is too cheap } else { // no side effects, reorder based on cost estimation @@ -908,8 +919,15 @@ private: // it's better to do the opposite for gzip purposes as well as for readability. auto* last = ptr->dynCast<Const>(); if (last) { - last->value = Literal(int32_t(last->value.geti32() + offset)); - offset = 0; + // don't do this if it would wrap the pointer + uint64_t value64 = last->value.geti32(); + uint64_t offset64 = offset; + if (value64 <= std::numeric_limits<int32_t>::max() && + offset64 <= std::numeric_limits<int32_t>::max() && + value64 + offset64 <= std::numeric_limits<int32_t>::max()) { + last->value = Literal(int32_t(value64 + offset64)); + offset = 0; + } } } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 346018c7d..88b967fdf 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -545,7 +545,7 @@ void WasmBinaryWriter::recurse(Expression*& curr) { } static bool brokenTo(Block* block) { - return block->name.is() && BranchUtils::BranchSeeker::has(block, block->name); + return block->name.is() && BranchUtils::BranchSeeker::hasNamed(block, block->name); } void WasmBinaryWriter::visitBlock(Block *curr) { @@ -636,7 +636,7 @@ int32_t WasmBinaryWriter::getBreakIndex(Name name) { // -1 if not found return breakStack.size() - 1 - i; } } - std::cerr << "bad break: " << name << std::endl; + std::cerr << "bad break: " << name << " in " << currFunction->name << std::endl; abort(); } @@ -2102,7 +2102,14 @@ void WasmBinaryBuilder::visitBlock(Block *curr) { } for (size_t i = start; i < end; i++) { if (debug) std::cerr << " " << size_t(expressionStack[i]) << "\n zz Block element " << curr->list.size() << std::endl; - curr->list.push_back(expressionStack[i]); + auto* item = expressionStack[i]; + curr->list.push_back(item); + if (i < end - 1) { + // stacky&unreachable code may introduce elements that need to be dropped in non-final positions + if (isConcreteWasmType(item->type)) { + curr->list.back() = Builder(wasm).makeDrop(curr->list.back()); + } + } } expressionStack.resize(start); curr->finalize(curr->type); diff --git a/test/passes/fuzz-exec_O.txt b/test/passes/fuzz-exec_O.txt new file mode 100644 index 000000000..f5f8583a3 --- /dev/null +++ b/test/passes/fuzz-exec_O.txt @@ -0,0 +1,25 @@ +[fuzz-exec] 2 results noted +(module + (type $0 (func (result i64))) + (type $1 (func (result i32))) + (memory $0 1 1) + (export "func_0" (func $func_0)) + (export "func_1" (func $func_1)) + (func $func_0 (type $0) (result i64) + (block $label$0 (result i64) + (br_if $label$0 + (i64.const 1234) + (i32.load16_s offset=22 align=1 + (i32.const -1) + ) + ) + ) + ) + (func $func_1 (type $1) (result i32) + (i32.load16_s offset=22 align=1 + (i32.const -1) + ) + ) +) +[fuzz-exec] 2 results noted +[fuzz-exec] results match diff --git a/test/passes/fuzz-exec_O.wast b/test/passes/fuzz-exec_O.wast new file mode 100644 index 000000000..3d03de714 --- /dev/null +++ b/test/passes/fuzz-exec_O.wast @@ -0,0 +1,23 @@ +(module + (memory $0 1 1) + (export "func_0" (func $func_0)) + (export "func_1" (func $func_1)) + (func $func_0 (result i64) + (block $label$0 (result i64) + (loop $label$1 (result i64) + (br_if $label$0 + (i64.const 1234) + (i32.load16_s offset=22 align=1 + (i32.const -1) + ) + ) + ) + ) + ) + (func $func_1 (result i32) + (i32.load16_s offset=22 align=1 + (i32.const -1) + ) + ) +) + diff --git a/test/passes/merge-blocks.txt b/test/passes/merge-blocks.txt index 545438b1d..b972a6421 100644 --- a/test/passes/merge-blocks.txt +++ b/test/passes/merge-blocks.txt @@ -78,10 +78,15 @@ ) (func $drop-unreachable-br_if (type $2) (result i32) (block $label$0 (result i32) - (block $label$2 - (drop - (br $label$0 - (i32.const 538976371) + (drop + (block $label$2 + (drop + (br_if $label$2 + (br $label$0 + (i32.const 538976371) + ) + (i32.const 1918987552) + ) ) ) ) @@ -100,4 +105,23 @@ ) ) ) + (func $br-goes-away-label2-becomes-unreachable (type $0) + (block $block + (drop + (block $label$1 (result i32) + (block $label$2 + (drop + (br_if $label$1 + (unreachable) + (i32.eqz + (br $label$2) + ) + ) + ) + ) + (i32.const 1) + ) + ) + ) + ) ) diff --git a/test/passes/merge-blocks.wast b/test/passes/merge-blocks.wast index e998060c4..81b70d1b5 100644 --- a/test/passes/merge-blocks.wast +++ b/test/passes/merge-blocks.wast @@ -82,5 +82,24 @@ ) ) ) + (func $br-goes-away-label2-becomes-unreachable + (block + (drop + (block $label$1 (result i32) + (block $label$2 + (drop + (br_if $label$1 + (unreachable) + (i32.eqz + (br $label$2) + ) + ) + ) + ) + (i32.const 1) + ) + ) + ) + ) ) diff --git a/test/passes/optimize-instructions.txt b/test/passes/optimize-instructions.txt index 604c9c3ea..a399a9096 100644 --- a/test/passes/optimize-instructions.txt +++ b/test/passes/optimize-instructions.txt @@ -6,6 +6,7 @@ (type $4 (func (param i32 i32))) (type $5 (func (param i32))) (type $6 (func (param i32 i32) (result i32))) + (type $7 (func (param i64) (result i64))) (memory $0 0) (export "load-off-2" (func $load-off-2)) (func $f (type $0) (param $i1 i32) (param $i2 i64) @@ -462,16 +463,16 @@ (i32.const 4) (get_local $0) ) - (i32.store - (i32.const 0) + (i32.store offset=2 + (i32.const -2) (get_local $0) ) (i32.store (i32.const 25) (get_local $0) ) - (i32.store - (i32.const -23) + (i32.store offset=2 + (i32.const -25) (get_local $0) ) (drop @@ -734,7 +735,7 @@ (i32.shr_s (i32.shl (i32.const 32) - (i32.const 59) + (i32.const 27) ) (i32.const 24) ) @@ -805,7 +806,7 @@ (i32.const -1) (i32.const 2147483647) ) - (i32.const 32) + (i32.const 31) ) ) (drop @@ -1083,19 +1084,19 @@ (drop (i32.shl (get_local $0) - (i32.const 211) + (i32.const 19) ) ) (drop (i32.shr_s (get_local $0) - (i32.const 211) + (i32.const 19) ) ) (drop (i32.shr_u (get_local $0) - (i32.const 211) + (i32.const 19) ) ) (drop @@ -2006,4 +2007,58 @@ (i32.const 1) ) ) + (func $neg-shifts-and-255 (type $2) (result i32) + (i32.and + (i32.shr_u + (i32.const -99) + (i32.const -32) + ) + (i32.const 255) + ) + ) + (func $neg-shifts-and-255-b (type $2) (result i32) + (i32.and + (i32.shl + (i32.const -2349025) + (i32.const -32) + ) + (i32.const 255) + ) + ) + (func $shifts-square-overflow (type $3) (param $x i32) (result i32) + (i32.shr_u + (i32.shr_u + (get_local $x) + (i32.const 65535) + ) + (i32.const 32767) + ) + ) + (func $shifts-square-no-overflow-small (type $3) (param $x i32) (result i32) + (i32.shr_u + (get_local $x) + (i32.const 9) + ) + ) + (func $shifts-square-overflow-64 (type $7) (param $x i64) (result i64) + (i64.shr_u + (i64.shr_u + (get_local $x) + (i64.const 65535) + ) + (i64.const 64767) + ) + ) + (func $shifts-square-no-overflow-small-64 (type $7) (param $x i64) (result i64) + (i64.shr_u + (get_local $x) + (i64.const 9) + ) + ) + (func $shifts-square-unreachable (type $3) (param $x i32) (result i32) + (i32.shr_u + (unreachable) + (i32.const 9) + ) + ) ) diff --git a/test/passes/optimize-instructions.wast b/test/passes/optimize-instructions.wast index cf8275412..8362453e9 100644 --- a/test/passes/optimize-instructions.wast +++ b/test/passes/optimize-instructions.wast @@ -842,7 +842,7 @@ (i32.const -1) (i32.const 2147483647) ) - (i32.const 32) + (i32.const 31) ;; adjusted after we fixed shift computation to just look at lower 5 bits ) (i32.const 24) ) @@ -2433,4 +2433,67 @@ (i32.const 1) ) ) + (func $neg-shifts-and-255 (result i32) + (i32.and + (i32.shr_u + (i32.const -99) + (i32.const -32) ;; this shift does nothing + ) + (i32.const 255) + ) + ) + (func $neg-shifts-and-255-b (result i32) + (i32.and + (i32.shl + (i32.const -2349025) + (i32.const -32) ;; this shift does nothing + ) + (i32.const 255) + ) + ) + (func $shifts-square-overflow (param $x i32) (result i32) + (i32.shr_u + (i32.shr_u + (get_local $x) + (i32.const 65535) ;; 31 bits effectively + ) + (i32.const 32767) ;; also 31 bits, so two shifts that force the value into nothing for sure + ) + ) + (func $shifts-square-no-overflow-small (param $x i32) (result i32) + (i32.shr_u + (i32.shr_u + (get_local $x) + (i32.const 1031) ;; 7 bits effectively + ) + (i32.const 4098) ;; 2 bits effectively + ) + ) + (func $shifts-square-overflow-64 (param $x i64) (result i64) + (i64.shr_u + (i64.shr_u + (get_local $x) + (i64.const 65535) ;; 63 bits effectively + ) + (i64.const 64767) ;; also 63 bits, so two shifts that force the value into nothing for sure + ) + ) + (func $shifts-square-no-overflow-small-64 (param $x i64) (result i64) + (i64.shr_u + (i64.shr_u + (get_local $x) + (i64.const 1031) ;; 7 bits effectively + ) + (i64.const 4098) ;; 2 bits effectively + ) + ) + (func $shifts-square-unreachable (param $x i32) (result i32) + (i32.shr_u + (i32.shr_u + (unreachable) + (i32.const 1031) ;; 7 bits effectively + ) + (i32.const 4098) ;; 2 bits effectively + ) + ) ) diff --git a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt index e2c4bb786..922f64008 100644 --- a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt +++ b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt @@ -251,4 +251,74 @@ (get_local $5) ) ) + (func $invalidate-conditionalizeExpensiveOnBitwise (type $0) (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (i32.and + (i32.lt_s + (i32.and + (i32.shr_s + (i32.shl + (i32.add + (get_local $1) + (i32.const -1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.const 255) + ) + (i32.const 3) + ) + (i32.ne + (tee_local $1 + (i32.const 0) + ) + (i32.const 0) + ) + ) + ) + (return + (get_local $0) + ) + ) + (return + (get_local $1) + ) + ) + (func $invalidate-conditionalizeExpensiveOnBitwise-ok (type $0) (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (if (result i32) + (tee_local $1 + (i32.const 0) + ) + (i32.lt_s + (i32.and + (i32.shr_s + (i32.shl + (i32.add + (get_local $0) + (i32.const -1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.const 255) + ) + (i32.const 3) + ) + (i32.const 0) + ) + ) + (return + (get_local $0) + ) + ) + (return + (get_local $1) + ) + ) ) diff --git a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast index e1215fa3f..7e8365812 100644 --- a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast +++ b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast @@ -259,5 +259,69 @@ (get_local $5) ) ) + (func $invalidate-conditionalizeExpensiveOnBitwise (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (i32.and + (i32.lt_s + (i32.and + (i32.shr_s + (i32.shl + (i32.add + (get_local $1) ;; conflict with tee + (i32.const -1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.const 255) + ) + (i32.const 3) + ) + (i32.ne + (tee_local $1 + (i32.const 0) + ) + (i32.const 0) + ) + ) + ) + (return (get_local $0)) + ) + (return (get_local $1)) + ) + (func $invalidate-conditionalizeExpensiveOnBitwise-ok (param $0 i32) (param $1 i32) (result i32) + (if + (i32.eqz + (i32.and + (i32.lt_s + (i32.and + (i32.shr_s + (i32.shl + (i32.add + (get_local $0) ;; no conflict + (i32.const -1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.const 255) + ) + (i32.const 3) + ) + (i32.ne + (tee_local $1 + (i32.const 0) + ) + (i32.const 0) + ) + ) + ) + (return (get_local $0)) + ) + (return (get_local $1)) + ) ) diff --git a/test/untaken-br_if.wast b/test/untaken-br_if.wast new file mode 100644 index 000000000..a165cf67f --- /dev/null +++ b/test/untaken-br_if.wast @@ -0,0 +1,14 @@ +(module + (func $binaryify-untaken-br_if (result f32) + (if + (i32.const 1) + (unreachable) + (block $label$1 + (br_if $label$1 + (i32.const 1) + (unreachable) + ) + ) + ) + ) +) diff --git a/test/untaken-br_if.wast.from-wast b/test/untaken-br_if.wast.from-wast new file mode 100644 index 000000000..2d6d9dd2d --- /dev/null +++ b/test/untaken-br_if.wast.from-wast @@ -0,0 +1,16 @@ +(module + (type $0 (func (result f32))) + (memory $0 0) + (func $binaryify-untaken-br_if (type $0) (result f32) + (if + (i32.const 1) + (unreachable) + (block $label$1 + (br_if $label$1 + (i32.const 1) + (unreachable) + ) + ) + ) + ) +) diff --git a/test/untaken-br_if.wast.fromBinary b/test/untaken-br_if.wast.fromBinary new file mode 100644 index 000000000..87dd9c757 --- /dev/null +++ b/test/untaken-br_if.wast.fromBinary @@ -0,0 +1,27 @@ +(module + (type $0 (func (result f32))) + (memory $0 0) + (func $binaryify-untaken-br_if (type $0) (result f32) + (if + (i32.const 1) + (block $label$0 + (unreachable) + ) + (block $label$1 + (block $label$2 + (drop + (i32.const 1) + ) + (br_if $label$2 + (unreachable) + ) + (unreachable) + (unreachable) + ) + (unreachable) + ) + ) + (unreachable) + ) +) + diff --git a/test/untaken-br_if.wast.fromBinary.noDebugInfo b/test/untaken-br_if.wast.fromBinary.noDebugInfo new file mode 100644 index 000000000..ad71cd874 --- /dev/null +++ b/test/untaken-br_if.wast.fromBinary.noDebugInfo @@ -0,0 +1,27 @@ +(module + (type $0 (func (result f32))) + (memory $0 0) + (func $0 (type $0) (result f32) + (if + (i32.const 1) + (block $label$0 + (unreachable) + ) + (block $label$1 + (block $label$2 + (drop + (i32.const 1) + ) + (br_if $label$2 + (unreachable) + ) + (unreachable) + (unreachable) + ) + (unreachable) + ) + ) + (unreachable) + ) +) + |