summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/utils.h66
-rw-r--r--test/passes/code-folding.txt4
-rw-r--r--test/passes/precompute.txt14
-rw-r--r--test/passes/remove-unused-brs.txt24
-rw-r--r--test/passes/remove-unused-brs.wast21
-rw-r--r--test/wasm2js/br.2asm.js16
6 files changed, 114 insertions, 31 deletions
diff --git a/src/ir/utils.h b/src/ir/utils.h
index 5eff0a52d..446e2c24a 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -83,13 +83,23 @@ struct ExpressionAnalyzer {
static uint32_t hash(Expression* curr);
};
-// Re-Finalizes all node types
+// Re-Finalizes all node types. This can be run after code was modified in
+// various ways that require propagating types around, and it does such an
+// "incremental" update. This is done under the assumption that there is
+// a valid assignment of types to apply.
// This removes "unnecessary' block/if/loop types, i.e., that are added
// specifically, as in
// (block (result i32) (unreachable))
// vs
// (block (unreachable))
// This converts to the latter form.
+// This also removes un-taken branches that would be a problem for
+// refinalization: if a block has been marked unreachable, and has
+// branches to it with values of type unreachable, then we don't
+// know the type for the block: it can't be none since the breaks
+// exist, but the breaks don't declare the type, rather everything
+// depends on the block. To avoid looking at the parent or something
+// else, just remove such un-taken branches.
struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<ReFinalize>>> {
bool isFunctionParallel() override { return true; }
@@ -108,7 +118,6 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
return;
}
// do this quickly, without any validation
- auto old = curr->type;
// last element determines type
curr->type = curr->list.back()->type;
// if concrete, it doesn't matter if we have an unreachable child, and we
@@ -121,14 +130,8 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
if (iter != breakValues.end()) {
// there is a break to here
auto type = iter->second;
- if (type == unreachable) {
- // all we have are breaks with values of type unreachable, and no
- // concrete fallthrough either. we must have had an existing type, then
- curr->type = old;
- assert(isConcreteType(curr->type));
- } else {
- curr->type = type;
- }
+ assert(type != unreachable); // we would have removed such branches
+ curr->type = type;
return;
}
}
@@ -147,15 +150,24 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
void visitLoop(Loop* curr) { curr->finalize(); }
void visitBreak(Break* curr) {
curr->finalize();
- updateBreakValueType(curr->name, getValueType(curr->value));
+ auto valueType = getValueType(curr->value);
+ if (valueType == unreachable) {
+ replaceUntaken(curr->value, curr->condition);
+ } else {
+ updateBreakValueType(curr->name, valueType);
+ }
}
void visitSwitch(Switch* curr) {
curr->finalize();
auto valueType = getValueType(curr->value);
- for (auto target : curr->targets) {
- updateBreakValueType(target, valueType);
+ if (valueType == unreachable) {
+ replaceUntaken(curr->value, curr->condition);
+ } else {
+ for (auto target : curr->targets) {
+ updateBreakValueType(target, valueType);
+ }
+ updateBreakValueType(curr->default_, valueType);
}
- updateBreakValueType(curr->default_, valueType);
}
void visitCall(Call* curr) { curr->finalize(); }
void visitCallIndirect(CallIndirect* curr) { curr->finalize(); }
@@ -195,6 +207,7 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
void visitMemory(Memory* curr) { WASM_UNREACHABLE(); }
void visitModule(Module* curr) { WASM_UNREACHABLE(); }
+private:
Type getValueType(Expression* value) {
return value ? value->type : none;
}
@@ -204,6 +217,31 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
breakValues[name] = type;
}
}
+
+ // Replace an untaken branch/switch with an unreachable value.
+ // A condition may also exist and may or may not be unreachable.
+ void replaceUntaken(Expression* value, Expression* condition) {
+ assert(value->type == unreachable);
+ auto* replacement = value;
+ if (condition) {
+ Builder builder(*getModule());
+ // Even if we have
+ // (block
+ // (unreachable)
+ // (i32.const 1)
+ // )
+ // we want the block type to be unreachable. That is valid as
+ // the value is unreachable, and necessary since the type of
+ // the condition did not have an impact before (the break/switch
+ // type was unreachable), and might not fit in.
+ if (isConcreteType(condition->type)) {
+ condition = builder.makeDrop(condition);
+ }
+ replacement = builder.makeSequence(value, condition);
+ assert(replacement->type);
+ }
+ replaceCurrent(replacement);
+ }
};
// Re-finalize a single node. This is slow, if you want to refinalize
diff --git a/test/passes/code-folding.txt b/test/passes/code-folding.txt
index 75b9dc3c8..8b5e14d7a 100644
--- a/test/passes/code-folding.txt
+++ b/test/passes/code-folding.txt
@@ -109,8 +109,8 @@
(func $leave-inner-block-type (; 6 ;) (type $1)
(block $label$1
(drop
- (block $label$2 (result i32)
- (br_if $label$2
+ (block $label$2
+ (block
(unreachable)
(unreachable)
)
diff --git a/test/passes/precompute.txt b/test/passes/precompute.txt
index 11bd93fc4..1c060fbef 100644
--- a/test/passes/precompute.txt
+++ b/test/passes/precompute.txt
@@ -162,7 +162,7 @@
(func $refinalize-two-breaks-one-unreachable (; 6 ;) (type $2)
(drop
(block $label$0 (result i64)
- (br_if $label$0
+ (block
(select
(i64.const 1)
(block $block
@@ -175,17 +175,21 @@
)
(i32.const 0)
)
- (i32.const 1)
+ (drop
+ (i32.const 1)
+ )
)
)
)
)
(func $one-break-value-and-it-is-unreachable (; 7 ;) (type $3) (result f64)
(local $var$0 i32)
- (block $label$6 (result f64)
- (br_if $label$6
+ (block $label$6
+ (block
(unreachable)
- (i32.const 0)
+ (drop
+ (i32.const 0)
+ )
)
)
)
diff --git a/test/passes/remove-unused-brs.txt b/test/passes/remove-unused-brs.txt
index 5db42249d..b5bcce9f2 100644
--- a/test/passes/remove-unused-brs.txt
+++ b/test/passes/remove-unused-brs.txt
@@ -1922,4 +1922,28 @@
)
)
)
+ (func $fuzz-block-unreachable-brs-with-values (; 80 ;) (type $2) (result i32)
+ (local $0 i32)
+ (loop $label$1
+ (block $label$2
+ (br_if $label$1
+ (i32.eqz
+ (get_local $0)
+ )
+ )
+ (tee_local $0
+ (loop $label$5
+ (br_if $label$5
+ (block
+ (unreachable)
+ (drop
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
)
diff --git a/test/passes/remove-unused-brs.wast b/test/passes/remove-unused-brs.wast
index c63dc4689..8325b47ac 100644
--- a/test/passes/remove-unused-brs.wast
+++ b/test/passes/remove-unused-brs.wast
@@ -1540,5 +1540,26 @@
(br $label$1)
)
)
+ (func $fuzz-block-unreachable-brs-with-values (result i32)
+ (local $0 i32)
+ (loop $label$1 (result i32)
+ (block $label$2 (result i32)
+ (if
+ (get_local $0)
+ (set_local $0
+ (loop $label$5
+ (br_if $label$5
+ (br_if $label$2
+ (unreachable)
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (br $label$1)
+ )
+ )
+ )
)
diff --git a/test/wasm2js/br.2asm.js b/test/wasm2js/br.2asm.js
index a877618a1..bea5cf57b 100644
--- a/test/wasm2js/br.2asm.js
+++ b/test/wasm2js/br.2asm.js
@@ -510,7 +510,7 @@ function asmFunc(global, env, buffer) {
}
function $54() {
- var $0 = 0, $1_1 = 0;
+ var $0 = 0;
block : {
block0 : {
$0 = 8;
@@ -523,10 +523,8 @@ function asmFunc(global, env, buffer) {
function $55() {
var $0 = 0, $1_1 = 0;
block : {
- block1 : {
- $0 = 8;
- break block;
- };
+ $0 = 8;
+ break block;
};
return 1 + $0 | 0 | 0;
}
@@ -541,12 +539,10 @@ function asmFunc(global, env, buffer) {
}
function $57() {
- var $0 = 0, $1_1 = 0;
+ var $0 = 0;
block : {
- block2 : {
- $0 = 8;
- break block;
- };
+ $0 = 8;
+ break block;
};
return 1 + $0 | 0 | 0;
}