summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xauto_update_tests.py2
-rwxr-xr-xcheck.py2
-rw-r--r--src/ast/bits.h20
-rw-r--r--src/ast/literal-utils.h26
-rw-r--r--src/passes/MergeBlocks.cpp34
-rw-r--r--src/passes/OptimizeInstructions.cpp46
-rw-r--r--src/wasm/wasm-binary.cpp13
-rw-r--r--test/passes/fuzz-exec_O.txt25
-rw-r--r--test/passes/fuzz-exec_O.wast23
-rw-r--r--test/passes/merge-blocks.txt32
-rw-r--r--test/passes/merge-blocks.wast19
-rw-r--r--test/passes/optimize-instructions.txt73
-rw-r--r--test/passes/optimize-instructions.wast65
-rw-r--r--test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt70
-rw-r--r--test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast64
-rw-r--r--test/untaken-br_if.wast14
-rw-r--r--test/untaken-br_if.wast.from-wast16
-rw-r--r--test/untaken-br_if.wast.fromBinary27
-rw-r--r--test/untaken-br_if.wast.fromBinary.noDebugInfo27
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):
diff --git a/check.py b/check.py
index 5c42c8812..105c177c7 100755
--- a/check.py
+++ b/check.py
@@ -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)
+ )
+)
+