summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/properties.h36
-rw-r--r--src/passes/OptimizeInstructions.cpp20
-rw-r--r--test/passes/optimize-instructions.txt91
-rw-r--r--test/passes/optimize-instructions.wast47
4 files changed, 176 insertions, 18 deletions
diff --git a/src/ir/properties.h b/src/ir/properties.h
index 7cbbe73f0..6848e9481 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -146,6 +146,42 @@ inline Index getZeroExtBits(Expression* curr) {
return Bits::getMaskedBits(curr->cast<Binary>()->right->cast<Const>()->value.geti32());
}
+// Returns a falling-through value, that is, it looks through a tee_local
+// and other operations that receive a value and let it flow through them.
+inline Expression* getFallthrough(Expression* curr) {
+ // If the current node is unreachable, there is no value
+ // falling through.
+ if (curr->type == unreachable) {
+ return curr;
+ }
+ if (auto* set = curr->dynCast<SetLocal>()) {
+ if (set->isTee()) {
+ return getFallthrough(set->value);
+ }
+ } else if (auto* block = curr->dynCast<Block>()) {
+ // if no name, we can't be broken to, and then can look at the fallthrough
+ if (!block->name.is() && block->list.size() > 0) {
+ return getFallthrough(block->list.back());
+ }
+ } else if (auto* loop = curr->dynCast<Loop>()) {
+ return getFallthrough(loop->body);
+ } else if (auto* iff = curr->dynCast<If>()) {
+ if (iff->ifFalse) {
+ // Perhaps just one of the two actually returns.
+ if (iff->ifTrue->type == unreachable) {
+ return getFallthrough(iff->ifFalse);
+ } else if (iff->ifFalse->type == unreachable) {
+ return getFallthrough(iff->ifTrue);
+ }
+ }
+ } else if (auto* br = curr->dynCast<Break>()) {
+ if (br->condition && br->value) {
+ return getFallthrough(br->value);
+ }
+ }
+ return curr;
+}
+
} // Properties
} // wasm
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 55042d96f..73f78611b 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -258,22 +258,6 @@ Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) {
}
}
-// looks through fallthrough operations, like tee_local, block fallthrough, etc.
-// too and block fallthroughs, etc.
-Expression* getFallthrough(Expression* curr) {
- if (auto* set = curr->dynCast<SetLocal>()) {
- if (set->isTee()) {
- return getFallthrough(set->value);
- }
- } else if (auto* block = curr->dynCast<Block>()) {
- // if no name, we can't be broken to, and then can look at the fallthrough
- if (!block->name.is() && block->list.size() > 0) {
- return getFallthrough(block->list.back());
- }
- }
- return curr;
-}
-
// Useful information about locals
struct LocalInfo {
static const Index kUnknown = Index(-1);
@@ -316,7 +300,7 @@ struct LocalScanner : PostWalker<LocalScanner> {
auto type = getFunction()->getLocalType(curr->index);
if (type != i32 && type != i64) return;
// an integer var, worth processing
- auto* value = getFallthrough(curr->value);
+ auto* value = Properties::getFallthrough(curr->value);
auto& info = localInfo[curr->index];
info.maxBits = std::max(info.maxBits, getMaxBits(value, this));
auto signExtBits = LocalInfo::kUnknown;
@@ -421,7 +405,7 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
Index extraShifts;
auto bits = Properties::getAlmostSignExtBits(binary, extraShifts);
if (extraShifts == 0) {
- if (auto* load = getFallthrough(ext)->dynCast<Load>()) {
+ if (auto* load = Properties::getFallthrough(ext)->dynCast<Load>()) {
// pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc.
if (LoadUtils::canBeSigned(load) &&
((load->bytes == 1 && bits == 8) || (load->bytes == 2 && bits == 16))) {
diff --git a/test/passes/optimize-instructions.txt b/test/passes/optimize-instructions.txt
index 0b156f803..5a0a45e3d 100644
--- a/test/passes/optimize-instructions.txt
+++ b/test/passes/optimize-instructions.txt
@@ -11,6 +11,7 @@
(type $9 (func (param i32 i64 f32 f64)))
(type $10 (func (param i32 i64 f32)))
(type $11 (func (param i32 i64 f64 i32)))
+ (type $12 (func (result f64)))
(memory $0 0)
(export "load-off-2" (func $load-off-2))
(func $f (; 0 ;) (type $0) (param $i1 i32) (param $i2 i64)
@@ -2906,6 +2907,96 @@
)
)
)
+ (func $getFallthrough (; 71 ;) (type $1)
+ (local $x0 i32)
+ (local $x1 i32)
+ (local $x2 i32)
+ (local $x3 i32)
+ (local $x4 i32)
+ (local $x5 i32)
+ (local $x6 i32)
+ (local $x7 i32)
+ (set_local $x0
+ (i32.const 1)
+ )
+ (drop
+ (get_local $x0)
+ )
+ (set_local $x1
+ (tee_local $x2
+ (i32.const 1)
+ )
+ )
+ (drop
+ (get_local $x1)
+ )
+ (set_local $x3
+ (loop $loop-in (result i32)
+ (i32.const 1)
+ )
+ )
+ (drop
+ (get_local $x3)
+ )
+ (set_local $x4
+ (if (result i32)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (i32.and
+ (get_local $x4)
+ (i32.const 7)
+ )
+ )
+ (set_local $x5
+ (if (result i32)
+ (i32.const 1)
+ (unreachable)
+ (i32.const 3)
+ )
+ )
+ (drop
+ (get_local $x5)
+ )
+ (set_local $x6
+ (if (result i32)
+ (i32.const 1)
+ (i32.const 3)
+ (unreachable)
+ )
+ )
+ (drop
+ (get_local $x6)
+ )
+ (drop
+ (block $out (result i32)
+ (set_local $x7
+ (br_if $out
+ (i32.const 1)
+ (i32.const 1)
+ )
+ )
+ (drop
+ (get_local $x7)
+ )
+ (unreachable)
+ )
+ )
+ )
+ (func $tee-with-unreachable-value (; 72 ;) (type $12) (result f64)
+ (local $var$0 i32)
+ (block $label$1 (result f64)
+ (tee_local $var$0
+ (br_if $label$1
+ (f64.const 1)
+ (unreachable)
+ )
+ )
+ )
+ )
)
(module
(type $0 (func))
diff --git a/test/passes/optimize-instructions.wast b/test/passes/optimize-instructions.wast
index b65432715..aa979c62a 100644
--- a/test/passes/optimize-instructions.wast
+++ b/test/passes/optimize-instructions.wast
@@ -3479,6 +3479,53 @@
)
)
)
+ (func $getFallthrough ;; unit tests for Properties::getFallthrough
+ (local $x0 i32)
+ (local $x1 i32)
+ (local $x2 i32)
+ (local $x3 i32)
+ (local $x4 i32)
+ (local $x5 i32)
+ (local $x6 i32)
+ (local $x7 i32)
+ ;; the trivial case
+ (set_local $x0 (i32.const 1))
+ (drop (i32.and (get_local $x0) (i32.const 7)))
+ ;; tees
+ (set_local $x1 (tee_local $x2 (i32.const 1)))
+ (drop (i32.and (get_local $x1) (i32.const 7)))
+ ;; loop
+ (set_local $x3 (loop (result i32) (i32.const 1)))
+ (drop (i32.and (get_local $x3) (i32.const 7)))
+ ;; if - two sides, can't
+ (set_local $x4 (if (result i32) (i32.const 1) (i32.const 2) (i32.const 3)))
+ (drop (i32.and (get_local $x4) (i32.const 7)))
+ ;; if - one side, can
+ (set_local $x5 (if (result i32) (i32.const 1) (unreachable) (i32.const 3)))
+ (drop (i32.and (get_local $x5) (i32.const 7)))
+ ;; if - one side, can
+ (set_local $x6 (if (result i32) (i32.const 1) (i32.const 3) (unreachable)))
+ (drop (i32.and (get_local $x6) (i32.const 7)))
+ ;; br_if with value
+ (drop
+ (block $out (result i32)
+ (set_local $x7 (br_if $out (i32.const 1) (i32.const 1)))
+ (drop (i32.and (get_local $x7) (i32.const 7)))
+ (unreachable)
+ )
+ )
+ )
+ (func $tee-with-unreachable-value (result f64)
+ (local $var$0 i32)
+ (block $label$1 (result f64)
+ (tee_local $var$0
+ (br_if $label$1 ;; the f64 does not actually flow through this, it's unreachable (and the type is wrong - but unchecked)
+ (f64.const 1)
+ (unreachable)
+ )
+ )
+ )
+ )
)
(module
(import "env" "memory" (memory $0 (shared 256 256)))