summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h7
-rw-r--r--src/ast/effects.h16
-rw-r--r--src/ast/properties.h24
-rw-r--r--src/passes/CoalesceLocals.cpp8
-rw-r--r--src/passes/MergeBlocks.cpp30
-rw-r--r--src/passes/OptimizeInstructions.cpp6
-rw-r--r--src/passes/Vacuum.cpp10
-rw-r--r--src/wasm-validator.h2
-rw-r--r--src/wasm/wasm-validator.cpp7
9 files changed, 87 insertions, 23 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 2245bd2b5..e2b1802b5 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -1397,7 +1397,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
void visitCallIndirect(CallIndirect* curr) {
// we already call into target = something + offset, where offset is a callImport with the name of the table. replace that with the table offset
// note that for an ftCall or mftCall, we have no asm.js mask, so have nothing to do here
- auto* add = curr->target->dynCast<Binary>();
+ auto* target = curr->target;
+ // might be a block with a fallthrough
+ if (auto* block = target->dynCast<Block>()) {
+ target = block->list.back();
+ }
+ auto* add = target->dynCast<Binary>();
if (!add) return;
if (add->right->is<CallImport>()) {
auto* offset = add->right->cast<CallImport>();
diff --git a/src/ast/effects.h b/src/ast/effects.h
index a7a5d8fb2..6e4bb617e 100644
--- a/src/ast/effects.h
+++ b/src/ast/effects.h
@@ -39,7 +39,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
if (breakNames.size() > 0) branches = true;
}
- bool branches = false; // branches out of this expression
+ bool branches = false; // branches out of this expression, returns, infinite loops, etc
bool calls = false;
std::set<Index> localsRead;
std::set<Index> localsWritten;
@@ -138,6 +138,18 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
}
void visitLoop(Loop* curr) {
if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
+ // if the loop is unreachable, then there is branching control flow:
+ // (1) if the body is unreachable because of a (return), uncaught (br) etc., then we
+ // already noted branching, so it is ok to mark it again (if we have *caught*
+ // (br)s, then they did not lead to the loop body being unreachable).
+ // (same logic applies to blocks)
+ // (2) if the loop is unreachable because it only has branches up to the loop
+ // top, but no way to get out, then it is an infinite loop, and we consider
+ // that a branching side effect (note how the same logic does not apply to
+ // blocks).
+ if (curr->type == unreachable) {
+ branches = true;
+ }
}
void visitCall(Call *curr) { calls = true; }
@@ -182,6 +194,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
case TruncUFloat64ToInt32:
case TruncUFloat64ToInt64: {
implicitTrap = true;
+ break;
}
default: {}
}
@@ -199,6 +212,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
case RemSInt64:
case RemUInt64: {
implicitTrap = true;
+ break;
}
default: {}
}
diff --git a/src/ast/properties.h b/src/ast/properties.h
index 9121deba5..097f7a8f0 100644
--- a/src/ast/properties.h
+++ b/src/ast/properties.h
@@ -60,11 +60,13 @@ struct Properties {
if (auto* outer = curr->dynCast<Binary>()) {
if (outer->op == ShrSInt32) {
if (auto* outerConst = outer->right->dynCast<Const>()) {
- if (auto* inner = outer->left->dynCast<Binary>()) {
- if (inner->op == ShlInt32) {
- if (auto* innerConst = inner->right->dynCast<Const>()) {
- if (outerConst->value == innerConst->value) {
- return inner->left;
+ if (outerConst->value.geti32() != 0) {
+ if (auto* inner = outer->left->dynCast<Binary>()) {
+ if (inner->op == ShlInt32) {
+ if (auto* innerConst = inner->right->dynCast<Const>()) {
+ if (outerConst->value == innerConst->value) {
+ return inner->left;
+ }
}
}
}
@@ -87,11 +89,13 @@ struct Properties {
if (auto* outer = curr->dynCast<Binary>()) {
if (outer->op == ShrSInt32) {
if (auto* outerConst = outer->right->dynCast<Const>()) {
- if (auto* inner = outer->left->dynCast<Binary>()) {
- if (inner->op == ShlInt32) {
- if (auto* innerConst = inner->right->dynCast<Const>()) {
- if (outerConst->value.leU(innerConst->value).geti32()) {
- return inner->left;
+ if (outerConst->value.geti32() != 0) {
+ if (auto* inner = outer->left->dynCast<Binary>()) {
+ if (inner->op == ShlInt32) {
+ if (auto* innerConst = inner->right->dynCast<Const>()) {
+ if (outerConst->value.leU(innerConst->value).geti32()) {
+ return inner->left;
+ }
}
}
}
diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp
index b36542a97..5ab68da23 100644
--- a/src/passes/CoalesceLocals.cpp
+++ b/src/passes/CoalesceLocals.cpp
@@ -196,7 +196,9 @@ struct CoalesceLocals : public WalkerPass<CFGWalker<CoalesceLocals, Visitor<Coal
if (auto* get = set->value->dynCast<GetLocal>()) return get;
if (auto* iff = set->value->dynCast<If>()) {
if (auto* get = iff->ifTrue->dynCast<GetLocal>()) return get;
- if (auto* get = iff->ifFalse->dynCast<GetLocal>()) return get;
+ if (iff->ifFalse) {
+ if (auto* get = iff->ifFalse->dynCast<GetLocal>()) return get;
+ }
}
return nullptr;
}
@@ -595,11 +597,13 @@ void CoalesceLocals::pickIndices(std::vector<Index>& indices) {
// Remove a copy from a set of an if, where one if arm is a get of the same set
static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression*& copy, Expression*& other, Module* module) {
// replace the origin with the if, and sink the set into the other non-copying arm
+ bool tee = set->isTee();
*origin = iff;
set->value = other;
set->finalize();
other = set;
- if (!isConcreteWasmType(set->type)) {
+ // if this is not a tee, then we can get rid of the copy in that arm
+ if (!tee) {
// we don't need the copy at all
copy = nullptr;
if (!iff->ifTrue) {
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 2c983d991..bc5fea6fb 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -188,6 +188,15 @@ static void optimizeBlock(Block* curr, Module* module) {
}
if (!child) continue;
if (child->name.is()) continue; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs)
+ if (child->type == unreachable) {
+ // an unreachable block can have a concrete final element (which is never reached)
+ if (!child->list.empty()) {
+ if (isConcreteWasmType(child->list.back()->type)) {
+ // just remove it
+ child->list.pop_back();
+ }
+ }
+ }
ExpressionList merged(module->allocator);
for (size_t j = 0; j < i; j++) {
merged.push_back(curr->list[j]);
@@ -279,11 +288,14 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
}
void visitSelect(Select* curr) {
+ // TODO: for now, just stop when we see any side effect. instead, we could
+ // check effects carefully for reordering
Block* outer = nullptr;
- outer = optimize(curr, curr->ifTrue, outer);
if (EffectAnalyzer(getPassOptions(), curr->ifTrue).hasSideEffects()) return;
- outer = optimize(curr, curr->ifFalse, outer);
+ outer = optimize(curr, curr->ifTrue, outer);
if (EffectAnalyzer(getPassOptions(), curr->ifFalse).hasSideEffects()) return;
+ outer = optimize(curr, curr->ifFalse, outer);
+ if (EffectAnalyzer(getPassOptions(), curr->condition).hasSideEffects()) return;
optimize(curr, curr->condition, outer);
}
@@ -299,11 +311,13 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
}
template<typename T>
- void handleCall(T* curr, Block* outer = nullptr) {
+ void handleCall(T* curr) {
+ Block* outer = nullptr;
for (Index i = 0; i < curr->operands.size(); i++) {
- outer = optimize(curr, curr->operands[i], outer);
if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return;
+ outer = optimize(curr, curr->operands[i], outer);
}
+ return;
}
void visitCall(Call* curr) {
@@ -315,9 +329,13 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
}
void visitCallIndirect(CallIndirect* curr) {
- auto* outer = optimize(curr, curr->target);
+ Block* outer = nullptr;
+ for (Index i = 0; i < curr->operands.size(); i++) {
+ if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return;
+ outer = optimize(curr, curr->operands[i], outer);
+ }
if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) return;
- handleCall(curr, outer);
+ optimize(curr, curr->target, outer);
}
};
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index d1b419a06..c6006d712 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -517,6 +517,12 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
}
}
}
+ // some operations have no effect TODO: many more
+ if (right->value == Literal(int32_t(0))) {
+ if (binary->op == ShlInt32 || binary->op == ShrUInt32 || binary->op == ShrSInt32) {
+ return binary->left;
+ }
+ }
// the square of some operations can be merged
if (auto* left = binary->left->dynCast<Binary>()) {
if (left->op == binary->op) {
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index 40f4a6e59..3f27fb36a 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -232,6 +232,16 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
replaceCurrent(child);
return;
}
+ // if the condition is unreachable, just return it
+ if (curr->condition->type == unreachable) {
+ typeUpdater.noteRecursiveRemoval(curr->ifTrue);
+ if (curr->ifFalse) {
+ typeUpdater.noteRecursiveRemoval(curr->ifFalse);
+ }
+ replaceCurrent(curr->condition);
+ return;
+ }
+ // from here on, we can assume the condition executed
if (curr->ifFalse) {
if (curr->ifFalse->is<Nop>()) {
curr->ifFalse = nullptr;
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index f778e6f24..a359102ea 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -220,7 +220,7 @@ public:
void shouldBeIntOrUnreachable(WasmType ty, Expression* curr, const char* text);
void validateAlignment(size_t align, WasmType type, Index bytes, bool isAtomic,
Expression* curr);
- void validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr);
+ void validateMemBytes(uint8_t bytes, WasmType type, Expression* curr);
void validateBinaryenIR(Module& wasm);
};
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 1b328ec19..5ca6b2783 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -261,14 +261,17 @@ void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement");
shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types");
}
-void WasmValidator::validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr) {
+void WasmValidator::validateMemBytes(uint8_t bytes, WasmType type, Expression* curr) {
+ if (type == unreachable) {
+ return; // nothing to validate in this case
+ }
switch (bytes) {
case 1:
case 2:
case 4:
break;
case 8: {
- shouldBeEqual(getWasmTypeSize(ty), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types");
+ shouldBeEqual(getWasmTypeSize(type), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types");
break;
}
default: fail("Memory operations must be 1,2,4, or 8 bytes", curr);