summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-07-19 14:38:24 -0700
committerGitHub <noreply@github.com>2021-07-19 14:38:24 -0700
commitfb9de9d391a7272548dcc41cd8229076189d7398 (patch)
treea1d5c3610ca8c3ec9b82e3f451e857b6470f75ac
parentf98eb3463cdfb1ad6a97dc0c4ba6d6d99f77e706 (diff)
downloadbinaryen-fb9de9d391a7272548dcc41cd8229076189d7398.tar.gz
binaryen-fb9de9d391a7272548dcc41cd8229076189d7398.tar.bz2
binaryen-fb9de9d391a7272548dcc41cd8229076189d7398.zip
RemoveUnusedBrs: Do not create a select with a multivalue result (#4005)
The spec disallows that. Fixes #3990
-rw-r--r--src/passes/RemoveUnusedBrs.cpp16
-rw-r--r--test/lit/passes/remove-unused-brs.wast47
2 files changed, 60 insertions, 3 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 5631c9d5f..157cfc1f3 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -97,6 +97,12 @@ static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions,
return total >= TOO_MUCH;
}
+static bool canEmitSelectWithArms(Expression* ifTrue, Expression* ifFalse) {
+ // A select only allows a single value in its arms in the spec:
+ // https://webassembly.github.io/spec/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-parametric-mathsf-select-t-ast
+ return ifTrue->type.isSingle() && ifFalse->type.isSingle();
+}
+
struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
bool isFunctionParallel() override { return true; }
@@ -1023,7 +1029,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
EffectAnalyzer(passOptions, features, curr)
.hasSideEffects();
list[0] = old;
- if (canReorder && !hasSideEffects) {
+ if (canReorder && !hasSideEffects &&
+ canEmitSelectWithArms(br->value, curr)) {
ExpressionManipulator::nop(list[0]);
replaceCurrent(
builder.makeSelect(br->condition, br->value, curr));
@@ -1044,8 +1051,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
// Convert an if into a select, if possible and beneficial to do so.
Select* selectify(If* iff) {
- if (!iff->ifFalse || !iff->ifTrue->type.isSingle() ||
- !iff->ifFalse->type.isSingle()) {
+ // Only an if-else can be turned into a select.
+ if (!iff->ifFalse) {
+ return nullptr;
+ }
+ if (!canEmitSelectWithArms(iff->ifTrue, iff->ifFalse)) {
return nullptr;
}
if (iff->condition->type == Type::unreachable) {
diff --git a/test/lit/passes/remove-unused-brs.wast b/test/lit/passes/remove-unused-brs.wast
index 089d82391..9ed319cb8 100644
--- a/test/lit/passes/remove-unused-brs.wast
+++ b/test/lit/passes/remove-unused-brs.wast
@@ -288,4 +288,51 @@
(call $get-i32)
)
)
+
+ ;; CHECK: (func $restructure-select-no-multivalue
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $block (result i32 i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_if $block
+ ;; CHECK-NEXT: (tuple.make
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: (call $restructure-br_if
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (tuple.make
+ ;; CHECK-NEXT: (i32.const 4)
+ ;; CHECK-NEXT: (i32.const 5)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $restructure-select-no-multivalue
+ (drop
+ (block $block (result i32 i32)
+ (drop
+ (br_if $block
+ (tuple.make
+ (i32.const 1)
+ ;; Add a side effect to prevent us turning $block into a
+ ;; restructured if - instead, we will try a restructured select.
+ ;; But, selects cannot return multiple values in the spec, so we
+ ;; can do nothing here.
+ (call $restructure-br_if
+ (i32.const 2)
+ )
+ )
+ (i32.const 3)
+ )
+ )
+ (tuple.make
+ (i32.const 4)
+ (i32.const 5)
+ )
+ )
+ )
+ )
)