summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/Precompute.cpp70
-rw-r--r--src/wasm-builder.h6
-rw-r--r--test/passes/O.txt18
-rw-r--r--test/passes/O.wast17
-rw-r--r--test/passes/precompute.txt78
-rw-r--r--test/passes/precompute.wast128
6 files changed, 300 insertions, 17 deletions
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 008058d58..bcfa4a980 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -26,7 +26,7 @@
namespace wasm {
-Name NONSTANDALONE("Binaryen|nonstandalone");
+Name NONSTANDALONE_FLOW("Binaryen|nonstandalone");
// Execute an expression by itself. Errors if we hit anything we need anything not in the expression itself standalone.
class StandaloneExpressionRunner : public ExpressionRunner<StandaloneExpressionRunner> {
@@ -36,38 +36,38 @@ public:
Flow visitLoop(Loop* curr) {
// loops might be infinite, so must be careful
// but we can't tell if non-infinite, since we don't have state, so loops are just impossible to optimize for now
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitCall(Call* curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitCallImport(CallImport* curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitCallIndirect(CallIndirect* curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitGetLocal(GetLocal *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitSetLocal(SetLocal *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitGetGlobal(GetGlobal *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitSetGlobal(SetGlobal *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitLoad(Load *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitStore(Store *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
Flow visitHost(Host *curr) {
- return Flow(NONSTANDALONE);
+ return Flow(NONSTANDALONE_FLOW);
}
void trap(const char* why) override {
@@ -89,7 +89,51 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi
} catch (StandaloneExpressionRunner::NonstandaloneException& e) {
return;
}
- if (flow.breaking()) return; // TODO: can create a break as a replacement in some cases (not NONSTANDALONE)
+ if (flow.breaking()) {
+ if (flow.breakTo == NONSTANDALONE_FLOW) return;
+ if (flow.breakTo == RETURN_FLOW) {
+ // this expression causes a return. if it's already a return, reuse the node
+ if (auto* ret = curr->dynCast<Return>()) {
+ if (flow.value.type != none) {
+ // reuse a const value if there is one
+ if (ret->value) {
+ if (auto* value = ret->value->dynCast<Const>()) {
+ value->value = flow.value;
+ return;
+ }
+ }
+ ret->value = Builder(*getModule()).makeConst(flow.value);
+ } else {
+ ret->value = nullptr;
+ }
+ } else {
+ Builder builder(*getModule());
+ replaceCurrent(builder.makeReturn(flow.value.type != none ? builder.makeConst(flow.value) : nullptr));
+ }
+ return;
+ }
+ // this expression causes a break, emit it directly. if it's already a br, reuse the node.
+ if (auto* br = curr->dynCast<Break>()) {
+ br->name = flow.breakTo;
+ br->condition = nullptr;
+ if (flow.value.type != none) {
+ // reuse a const value if there is one
+ if (br->value) {
+ if (auto* value = br->value->dynCast<Const>()) {
+ value->value = flow.value;
+ return;
+ }
+ }
+ br->value = Builder(*getModule()).makeConst(flow.value);
+ } else {
+ br->value = nullptr;
+ }
+ } else {
+ Builder builder(*getModule());
+ replaceCurrent(builder.makeBreak(flow.breakTo, flow.value.type != none ? builder.makeConst(flow.value) : nullptr));
+ }
+ return;
+ }
// this was precomputed
if (isConcreteWasmType(flow.value.type)) {
replaceCurrent(Builder(*getModule()).makeConst(flow.value));
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 546d72391..7cb195d08 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -76,7 +76,7 @@ public:
}
return ret;
}
- If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse=nullptr) {
+ If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse = nullptr) {
auto* ret = allocator.alloc<If>();
ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse;
ret->finalize();
@@ -88,7 +88,7 @@ public:
ret->finalize();
return ret;
}
- Break* makeBreak(Name name, Expression* value=nullptr, Expression* condition=nullptr) {
+ Break* makeBreak(Name name, Expression* value = nullptr, Expression* condition = nullptr) {
auto* ret = allocator.alloc<Break>();
ret->name = name; ret->value = value; ret->condition = condition;
ret->finalize();
@@ -202,7 +202,7 @@ public:
ret->finalize();
return ret;
}
- Return* makeReturn(Expression *value) {
+ Return* makeReturn(Expression *value = nullptr) {
auto* ret = allocator.alloc<Return>();
ret->value = value;
return ret;
diff --git a/test/passes/O.txt b/test/passes/O.txt
new file mode 100644
index 000000000..a70afc81a
--- /dev/null
+++ b/test/passes/O.txt
@@ -0,0 +1,18 @@
+(module
+ (memory 0)
+ (type $0 (func (result i32)))
+ (func $ret (type $0) (result i32)
+ (block $out i32
+ (drop
+ (call $ret)
+ )
+ (if
+ (call $ret)
+ (return
+ (i32.const 1)
+ )
+ )
+ (i32.const 999)
+ )
+ )
+)
diff --git a/test/passes/O.wast b/test/passes/O.wast
new file mode 100644
index 000000000..8b94d39fc
--- /dev/null
+++ b/test/passes/O.wast
@@ -0,0 +1,17 @@
+(module
+ (func $ret (result i32)
+ (block $out
+ (drop (call $ret))
+ (if (call $ret)
+ (return
+ (return
+ (i32.const 1)
+ )
+ )
+ )
+ (drop (br_if $out (i32.const 999) (i32.const 1)))
+ (unreachable)
+ )
+ )
+)
+
diff --git a/test/passes/precompute.txt b/test/passes/precompute.txt
index cbf0f23a7..48a6c0f4d 100644
--- a/test/passes/precompute.txt
+++ b/test/passes/precompute.txt
@@ -1,6 +1,8 @@
(module
(memory 0)
(type $0 (func (param i32)))
+ (type $1 (func (result i32)))
+ (type $2 (func))
(func $x (type $0) (param $x i32)
(nop)
(drop
@@ -21,9 +23,83 @@
(call $x
(i32.const 4)
)
- (br_if $c
+ (br $c)
+ (br $c)
+ )
+ (drop
+ (block $val i32
+ (nop)
+ (call $x
+ (i32.const 4)
+ )
+ (br $val
+ (i32.const 101)
+ )
+ (br $val
+ (i32.const 102)
+ )
+ )
+ )
+ (nop)
+ (drop
+ (block $d i32
+ (call $x
+ (i32.const 5)
+ )
+ (nop)
(i32.const 1)
)
)
+ (drop
+ (block $d i32
+ (call $x
+ (i32.const 6)
+ )
+ (nop)
+ (i32.const 1)
+ )
+ )
+ (drop
+ (block $d i32
+ (call $x
+ (i32.const 7)
+ )
+ (nop)
+ (i32.const 2)
+ )
+ )
+ (call $x
+ (i32.const 2)
+ )
+ (call $x
+ (i32.const 1)
+ )
+ (call $x
+ (i32.const 0)
+ )
+ (call $x
+ (i32.const 0)
+ )
+ )
+ (func $ret (type $1) (result i32)
+ (if
+ (call $ret)
+ (return
+ (i32.const 0)
+ )
+ )
+ (if
+ (call $ret)
+ (return
+ (i32.const 1)
+ )
+ )
+ (i32.const 1)
+ )
+ (func $noret (type $2)
+ (if
+ (call $ret)
+ (return)
+ )
)
)
diff --git a/test/passes/precompute.wast b/test/passes/precompute.wast
index e57522578..17b02b336 100644
--- a/test/passes/precompute.wast
+++ b/test/passes/precompute.wast
@@ -50,6 +50,134 @@
(br_if $c (i32.const 0))
(call $x (i32.const 4))
(br_if $c (i32.const 1))
+ (br $c)
+ )
+ (drop
+ (block $val
+ (drop (br_if $val (i32.const 100) (i32.const 0)))
+ (call $x (i32.const 4))
+ (drop (br_if $val (i32.const 101) (i32.const 1)))
+ (br $val (i32.const 102))
+ )
+ )
+ (block $d
+ (block $e
+ (br_if $d (br $e))
+ (call $x (i32.const 4))
+ (br_if $e (br $d))
+ )
+ )
+ (drop
+ (block $d
+ (call $x (i32.const 5))
+ (block $e
+ (drop (br_if $d (br $e) (i32.const 1)))
+ (drop (br_if $d (br $e) (i32.const 0)))
+ (drop (br_if $d (i32.const 1) (br $e)))
+ (drop (br_if $d (i32.const 0) (br $e)))
+ (unreachable)
+ )
+ (i32.const 1)
+ )
+ )
+ (drop
+ (block $d
+ (call $x (i32.const 6))
+ (block $e
+ (drop (br_if $d (br $e) (i32.const 0)))
+ (drop (br_if $d (i32.const 1) (br $e)))
+ (drop (br_if $d (i32.const 0) (br $e)))
+ (unreachable)
+ )
+ (i32.const 1)
+ )
+ )
+ (drop
+ (block $d
+ (call $x (i32.const 7))
+ (block $e
+ (drop (br_if $d (i32.const 1) (br $e)))
+ )
+ (i32.const 2)
+ )
+ )
+ (call $x
+ (block $out
+ (block $waka1
+ (block $waka2
+ (block $waka3
+ (br_table $waka1 $waka2 $waka3
+ (i32.const 0)
+ )
+ )
+ (br $out (i32.const 0))
+ )
+ (br $out (i32.const 1))
+ )
+ (br $out (i32.const 2))
+ )
+ )
+ (call $x
+ (block $out
+ (block $waka1
+ (block $waka2
+ (block $waka3
+ (br_table $waka1 $waka2 $waka3
+ (i32.const 1)
+ )
+ )
+ (br $out (i32.const 0))
+ )
+ (br $out (i32.const 1))
+ )
+ (br $out (i32.const 2))
+ )
+ )
+ (call $x
+ (block $out
+ (block $waka1
+ (block $waka2
+ (block $waka3
+ (br_table $waka1 $waka2 $waka3
+ (i32.const 2)
+ )
+ )
+ (br $out (i32.const 0))
+ )
+ (br $out (i32.const 1))
+ )
+ (br $out (i32.const 2))
+ )
+ )
+ (call $x
+ (block $out
+ (block $waka1
+ (block $waka2
+ (block $waka3
+ (br_table $waka1 $waka2 $waka3
+ (i32.const 3)
+ )
+ )
+ (br $out (i32.const 0))
+ )
+ (br $out (i32.const 1))
+ )
+ (br $out (i32.const 2))
+ )
+ )
+ )
+ (func $ret (result i32)
+ (if (call $ret)
+ (return (i32.const 0))
+ )
+ (if (call $ret)
+ (return (return (i32.const 1)))
+ )
+ (i32.const 1)
+ )
+ (func $noret
+ (if (call $ret)
+ (return)
)
)
)