summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/cost.h12
-rw-r--r--src/ir/properties.h30
-rw-r--r--src/passes/AvoidReinterprets.cpp29
-rw-r--r--src/passes/OptimizeInstructions.cpp20
-rw-r--r--src/passes/Precompute.cpp3
-rw-r--r--test/passes/optimize-instructions_all-features.txt50
-rw-r--r--test/passes/optimize-instructions_all-features.wast38
-rw-r--r--test/passes/remove-unused-names_optimize-instructions_all-features.txt104
-rw-r--r--test/passes/remove-unused-names_optimize-instructions_all-features.wast78
9 files changed, 309 insertions, 55 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 37f2611dd..e89fb9d17 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -726,6 +726,18 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> {
Index visitDrop(Drop* curr) { return visit(curr->value); }
Index visitReturn(Return* curr) { return maybeVisit(curr->value); }
Index visitHost(Host* curr) { return 100; }
+ Index visitRefNull(RefNull* curr) { return 1; }
+ Index visitRefIsNull(RefIsNull* curr) { return 1; }
+ Index visitRefFunc(RefFunc* curr) { return 1; }
+ Index visitTry(Try* curr) {
+ // We assume no exception will be thrown in most cases
+ return visit(curr->body);
+ }
+ Index visitThrow(Throw* curr) { return 100; }
+ Index visitRethrow(Rethrow* curr) { return 100; }
+ Index visitBrOnExn(BrOnExn* curr) {
+ return 1 + visit(curr->exnref) + curr->sent.size();
+ }
Index visitNop(Nop* curr) { return 0; }
Index visitUnreachable(Unreachable* curr) { return 0; }
};
diff --git a/src/ir/properties.h b/src/ir/properties.h
index 01e78563d..1dbb0e096 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -18,6 +18,8 @@
#define wasm_ir_properties_h
#include "ir/bits.h"
+#include "ir/effects.h"
+#include "ir/iteration.h"
#include "wasm.h"
namespace wasm {
@@ -68,6 +70,10 @@ inline bool isNamedControlFlow(Expression* curr) {
return false;
}
+inline bool isConstantExpression(const Expression* curr) {
+ return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>();
+}
+
// Check if an expression is a sign-extend, and if so, returns the value
// that is extended, otherwise nullptr
inline Expression* getSignExtValue(Expression* curr) {
@@ -153,7 +159,9 @@ inline Index getZeroExtBits(Expression* curr) {
// Returns a falling-through value, that is, it looks through a local.tee
// and other operations that receive a value and let it flow through them.
-inline Expression* getFallthrough(Expression* curr) {
+inline Expression* getFallthrough(Expression* curr,
+ const PassOptions& passOptions,
+ FeatureSet features) {
// If the current node is unreachable, there is no value
// falling through.
if (curr->type == Type::unreachable) {
@@ -161,36 +169,36 @@ inline Expression* getFallthrough(Expression* curr) {
}
if (auto* set = curr->dynCast<LocalSet>()) {
if (set->isTee()) {
- return getFallthrough(set->value);
+ return getFallthrough(set->value, passOptions, features);
}
} 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 getFallthrough(block->list.back(), passOptions, features);
}
} else if (auto* loop = curr->dynCast<Loop>()) {
- return getFallthrough(loop->body);
+ return getFallthrough(loop->body, passOptions, features);
} else if (auto* iff = curr->dynCast<If>()) {
if (iff->ifFalse) {
// Perhaps just one of the two actually returns.
if (iff->ifTrue->type == Type::unreachable) {
- return getFallthrough(iff->ifFalse);
+ return getFallthrough(iff->ifFalse, passOptions, features);
} else if (iff->ifFalse->type == Type::unreachable) {
- return getFallthrough(iff->ifTrue);
+ return getFallthrough(iff->ifTrue, passOptions, features);
}
}
} else if (auto* br = curr->dynCast<Break>()) {
if (br->condition && br->value) {
- return getFallthrough(br->value);
+ return getFallthrough(br->value, passOptions, features);
+ }
+ } else if (auto* tryy = curr->dynCast<Try>()) {
+ if (!EffectAnalyzer(passOptions, features, tryy->body).throws) {
+ return getFallthrough(tryy->body, passOptions, features);
}
}
return curr;
}
-inline bool isConstantExpression(const Expression* curr) {
- return curr->is<Const>() || curr->is<RefNull>() || curr->is<RefFunc>();
-}
-
} // namespace Properties
} // namespace wasm
diff --git a/src/passes/AvoidReinterprets.cpp b/src/passes/AvoidReinterprets.cpp
index 4df75dd15..91a20912d 100644
--- a/src/passes/AvoidReinterprets.cpp
+++ b/src/passes/AvoidReinterprets.cpp
@@ -36,7 +36,10 @@ static bool canReplaceWithReinterpret(Load* load) {
load->bytes == load->type.getByteSize();
}
-static Load* getSingleLoad(LocalGraph* localGraph, LocalGet* get) {
+static Load* getSingleLoad(LocalGraph* localGraph,
+ LocalGet* get,
+ const PassOptions& passOptions,
+ FeatureSet features) {
std::set<LocalGet*> seen;
seen.insert(get);
while (1) {
@@ -48,7 +51,7 @@ static Load* getSingleLoad(LocalGraph* localGraph, LocalGet* get) {
if (!set) {
return nullptr;
}
- auto* value = Properties::getFallthrough(set->value);
+ auto* value = Properties::getFallthrough(set->value, passOptions, features);
if (auto* parentGet = value->dynCast<LocalGet>()) {
if (seen.count(parentGet)) {
// We are in a cycle of gets, in unreachable code.
@@ -98,9 +101,12 @@ struct AvoidReinterprets : public WalkerPass<PostWalker<AvoidReinterprets>> {
void visitUnary(Unary* curr) {
if (isReinterpret(curr)) {
+ FeatureSet features = getModule()->features;
if (auto* get =
- Properties::getFallthrough(curr->value)->dynCast<LocalGet>()) {
- if (auto* load = getSingleLoad(localGraph, get)) {
+ Properties::getFallthrough(curr->value, getPassOptions(), features)
+ ->dynCast<LocalGet>()) {
+ if (auto* load =
+ getSingleLoad(localGraph, get, getPassOptions(), features)) {
auto& info = infos[load];
info.reinterpreted = true;
}
@@ -130,22 +136,27 @@ struct AvoidReinterprets : public WalkerPass<PostWalker<AvoidReinterprets>> {
std::map<Load*, Info>& infos;
LocalGraph* localGraph;
Module* module;
+ const PassOptions& passOptions;
FinalOptimizer(std::map<Load*, Info>& infos,
LocalGraph* localGraph,
- Module* module)
- : infos(infos), localGraph(localGraph), module(module) {}
+ Module* module,
+ const PassOptions& passOptions)
+ : infos(infos), localGraph(localGraph), module(module),
+ passOptions(passOptions) {}
void visitUnary(Unary* curr) {
if (isReinterpret(curr)) {
- auto* value = Properties::getFallthrough(curr->value);
+ auto* value = Properties::getFallthrough(
+ curr->value, passOptions, module->features);
if (auto* load = value->dynCast<Load>()) {
// A reinterpret of a load - flip it right here if we can.
if (canReplaceWithReinterpret(load)) {
replaceCurrent(makeReinterpretedLoad(load, load->ptr));
}
} else if (auto* get = value->dynCast<LocalGet>()) {
- if (auto* load = getSingleLoad(localGraph, get)) {
+ if (auto* load = getSingleLoad(
+ localGraph, get, passOptions, module->features)) {
auto iter = infos.find(load);
if (iter != infos.end()) {
auto& info = iter->second;
@@ -188,7 +199,7 @@ struct AvoidReinterprets : public WalkerPass<PostWalker<AvoidReinterprets>> {
ptr,
load->type.reinterpret());
}
- } finalOptimizer(infos, localGraph, getModule());
+ } finalOptimizer(infos, localGraph, getModule(), getPassOptions());
finalOptimizer.walk(func->body);
}
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 90abed825..3e3863e95 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -200,8 +200,11 @@ struct LocalInfo {
struct LocalScanner : PostWalker<LocalScanner> {
std::vector<LocalInfo>& localInfo;
+ const PassOptions& passOptions;
- LocalScanner(std::vector<LocalInfo>& localInfo) : localInfo(localInfo) {}
+ LocalScanner(std::vector<LocalInfo>& localInfo,
+ const PassOptions& passOptions)
+ : localInfo(localInfo), passOptions(passOptions) {}
void doWalkFunction(Function* func) {
// prepare
@@ -236,7 +239,8 @@ struct LocalScanner : PostWalker<LocalScanner> {
return;
}
// an integer var, worth processing
- auto* value = Properties::getFallthrough(curr->value);
+ auto* value = Properties::getFallthrough(
+ curr->value, passOptions, getModule()->features);
auto& info = localInfo[curr->index];
info.maxBits = std::max(info.maxBits, getMaxBits(value, this));
auto signExtBits = LocalInfo::kUnknown;
@@ -289,7 +293,8 @@ struct OptimizeInstructions
void doWalkFunction(Function* func) {
// first, scan locals
{
- LocalScanner scanner(localInfo);
+ LocalScanner scanner(localInfo, getPassOptions());
+ scanner.setModule(getModule());
scanner.walkFunction(func);
}
// main walk
@@ -347,7 +352,9 @@ struct OptimizeInstructions
Index extraShifts;
auto bits = Properties::getAlmostSignExtBits(binary, extraShifts);
if (extraShifts == 0) {
- if (auto* load = Properties::getFallthrough(ext)->dynCast<Load>()) {
+ if (auto* load =
+ Properties::getFallthrough(ext, getPassOptions(), features)
+ ->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) &&
@@ -984,6 +991,11 @@ private:
} else if (auto* select = boolean->dynCast<Select>()) {
select->ifTrue = optimizeBoolean(select->ifTrue);
select->ifFalse = optimizeBoolean(select->ifFalse);
+ } else if (auto* tryy = boolean->dynCast<Try>()) {
+ if (tryy->type == Type::i32) {
+ tryy->body = optimizeBoolean(tryy->body);
+ tryy->catchBody = optimizeBoolean(tryy->catchBody);
+ }
}
// TODO: recurse into br values?
return boolean;
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 21393c1cf..3a886166c 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -327,7 +327,8 @@ private:
continue; // already known constant
}
auto value = setValues[set] =
- precomputeValue(Properties::getFallthrough(set->value));
+ precomputeValue(Properties::getFallthrough(
+ set->value, getPassOptions(), getModule()->features));
if (value.isConcrete()) {
for (auto* get : localGraph.setInfluences[set]) {
work.insert(get);
diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt
index 53c28e896..1df99cc21 100644
--- a/test/passes/optimize-instructions_all-features.txt
+++ b/test/passes/optimize-instructions_all-features.txt
@@ -13,6 +13,7 @@
(type $i32_i64_f32_f64_=>_none (func (param i32 i64 f32 f64)))
(type $i32_i64_f64_i32_=>_none (func (param i32 i64 f64 i32)))
(type $none_=>_f64 (func (result f64)))
+ (type $none_=>_anyref (func (result anyref)))
(memory $0 0)
(export "load-off-2" (func $load-off-2))
(func $f (; 0 ;) (param $i1 i32) (param $i2 i64)
@@ -216,6 +217,18 @@
(i32.const 123)
(nop)
)
+ (if
+ (try (result i32)
+ (i32.const 123)
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.const 456)
+ )
+ )
+ (nop)
+ )
(drop
(select
(i32.const 102)
@@ -436,7 +449,7 @@
)
(drop
(i32.and
- (block $block3 (result i32)
+ (block $block4 (result i32)
(i32.const -6)
)
(local.get $x)
@@ -444,7 +457,7 @@
)
(drop
(i32.and
- (block $block4 (result i32)
+ (block $block5 (result i32)
(i32.const 5)
)
(loop $loop-in (result i32)
@@ -454,20 +467,20 @@
)
(drop
(i32.and
- (block $block6 (result i32)
+ (block $block7 (result i32)
(i32.const 8)
)
- (loop $loop-in5 (result i32)
+ (loop $loop-in6 (result i32)
(i32.const 7)
)
)
)
(drop
(i32.and
- (block $block8 (result i32)
+ (block $block9 (result i32)
(i32.const 10)
)
- (loop $loop-in7 (result i32)
+ (loop $loop-in8 (result i32)
(call $and-pos1)
(i32.const 9)
)
@@ -475,22 +488,22 @@
)
(drop
(i32.and
- (block $block10 (result i32)
+ (block $block11 (result i32)
(call $and-pos1)
(i32.const 12)
)
- (loop $loop-in9 (result i32)
+ (loop $loop-in10 (result i32)
(i32.const 11)
)
)
)
(drop
(i32.and
- (loop $loop-in11 (result i32)
+ (loop $loop-in12 (result i32)
(call $and-pos1)
(i32.const 13)
)
- (block $block12 (result i32)
+ (block $block13 (result i32)
(call $and-pos1)
(i32.const 14)
)
@@ -498,11 +511,11 @@
)
(drop
(i32.and
- (block $block13 (result i32)
+ (block $block14 (result i32)
(call $and-pos1)
(i32.const 14)
)
- (loop $loop-in14 (result i32)
+ (loop $loop-in15 (result i32)
(call $and-pos1)
(i32.const 13)
)
@@ -510,7 +523,7 @@
)
(drop
(i32.and
- (block $block15 (result i32)
+ (block $block16 (result i32)
(i32.const 15)
)
(local.get $x)
@@ -518,7 +531,7 @@
)
(drop
(i32.and
- (block $block16 (result i32)
+ (block $block17 (result i32)
(i32.const 15)
)
(local.get $x)
@@ -3319,6 +3332,9 @@
(unreachable)
)
)
+ (func $if-arms-subtype (; 78 ;) (result anyref)
+ (ref.null)
+ )
)
(module
(type $none_=>_none (func))
@@ -3337,9 +3353,3 @@
)
)
)
-(module
- (type $none_=>_anyref (func (result anyref)))
- (func $test (; 0 ;) (result anyref)
- (ref.null)
- )
-)
diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast
index 75993effa..5078e74c5 100644
--- a/test/passes/optimize-instructions_all-features.wast
+++ b/test/passes/optimize-instructions_all-features.wast
@@ -238,6 +238,26 @@
)
(nop)
)
+ (if
+ (try (result i32)
+ (i32.eqz
+ (i32.eqz
+ (i32.const 123)
+ )
+ )
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.eqz
+ (i32.eqz
+ (i32.const 456)
+ )
+ )
+ )
+ )
+ (nop)
+ )
(drop
(select
(i32.const 101)
@@ -3766,6 +3786,14 @@
(unreachable)
)
)
+ ;; Tests when if arms are subtype of if's type
+ (func $if-arms-subtype (result anyref)
+ (if (result anyref)
+ (i32.const 0)
+ (ref.null)
+ (ref.null)
+ )
+ )
)
(module
(import "env" "memory" (memory $0 (shared 256 256)))
@@ -3783,13 +3811,3 @@
)
)
)
-(module
- ;; Tests when if arms are subtype of if's type
- (func $test (result anyref)
- (if (result anyref)
- (i32.const 0)
- (ref.null)
- (ref.null)
- )
- )
-)
diff --git a/test/passes/remove-unused-names_optimize-instructions_all-features.txt b/test/passes/remove-unused-names_optimize-instructions_all-features.txt
new file mode 100644
index 000000000..03c30c6c3
--- /dev/null
+++ b/test/passes/remove-unused-names_optimize-instructions_all-features.txt
@@ -0,0 +1,104 @@
+(module
+ (type $none_=>_none (func))
+ (type $i32_=>_none (func (param i32)))
+ (event $e (attr 0) (param i32))
+ (func $dummy (; 0 ;)
+ (nop)
+ )
+ (func $getFallthrough (; 1 ;)
+ (local $x0 i32)
+ (local $x1 i32)
+ (local $x2 i32)
+ (local $x3 i32)
+ (local.set $x0
+ (try (result i32)
+ (i32.const 1)
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (local.get $x0)
+ )
+ (local.set $x1
+ (try (result i32)
+ (block (result i32)
+ (call $dummy)
+ (i32.const 1)
+ )
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (i32.and
+ (local.get $x1)
+ (i32.const 7)
+ )
+ )
+ (local.set $x2
+ (try (result i32)
+ (block (result i32)
+ (try
+ (throw $e
+ (i32.const 0)
+ )
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ )
+ )
+ (i32.const 1)
+ )
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (local.get $x2)
+ )
+ (local.set $x3
+ (try (result i32)
+ (block (result i32)
+ (try
+ (nop)
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (throw $e
+ (i32.const 0)
+ )
+ )
+ )
+ (i32.const 1)
+ )
+ (catch
+ (drop
+ (exnref.pop)
+ )
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (i32.and
+ (local.get $x3)
+ (i32.const 7)
+ )
+ )
+ )
+)
diff --git a/test/passes/remove-unused-names_optimize-instructions_all-features.wast b/test/passes/remove-unused-names_optimize-instructions_all-features.wast
new file mode 100644
index 000000000..bb8f90074
--- /dev/null
+++ b/test/passes/remove-unused-names_optimize-instructions_all-features.wast
@@ -0,0 +1,78 @@
+(module
+ (func $dummy)
+ (event $e (attr 0) (param i32))
+
+ (func $getFallthrough ;; unit tests for Properties::getFallthrough
+ (local $x0 i32)
+ (local $x1 i32)
+ (local $x2 i32)
+ (local $x3 i32)
+
+ ;; try - try body does not throw, can
+ (local.set $x0
+ (try (result i32)
+ (i32.const 1)
+ (catch
+ (drop (exnref.pop))
+ (i32.const 3)
+ )
+ )
+ )
+ (drop (i32.and (local.get $x0) (i32.const 7)))
+
+ ;; try - try body may throw, can't
+ (local.set $x1
+ (try (result i32)
+ (block (result i32)
+ (call $dummy)
+ (i32.const 1)
+ )
+ (catch
+ (drop (exnref.pop))
+ (i32.const 3)
+ )
+ )
+ )
+ (drop (i32.and (local.get $x1) (i32.const 7)))
+
+ ;; nested try - inner try may throw but will be caught by inner catch, can
+ (local.set $x2
+ (try (result i32)
+ (block (result i32)
+ (try
+ (throw $e (i32.const 0))
+ (catch
+ (drop (exnref.pop))
+ )
+ )
+ (i32.const 1)
+ )
+ (catch
+ (drop (exnref.pop))
+ (i32.const 3)
+ )
+ )
+ )
+ (drop (i32.and (local.get $x2) (i32.const 7)))
+
+ ;; nested try - inner catch may throw, can't
+ (local.set $x3
+ (try (result i32)
+ (block (result i32)
+ (try
+ (catch
+ (drop (exnref.pop))
+ (throw $e (i32.const 0))
+ )
+ )
+ (i32.const 1)
+ )
+ (catch
+ (drop (exnref.pop))
+ (i32.const 3)
+ )
+ )
+ )
+ (drop (i32.and (local.get $x3) (i32.const 7)))
+ )
+)