diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 7 | ||||
-rw-r--r-- | src/ast/effects.h | 16 | ||||
-rw-r--r-- | src/ast/properties.h | 24 | ||||
-rw-r--r-- | src/passes/CoalesceLocals.cpp | 8 | ||||
-rw-r--r-- | src/passes/MergeBlocks.cpp | 30 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 6 | ||||
-rw-r--r-- | src/passes/Vacuum.cpp | 10 | ||||
-rw-r--r-- | src/wasm-validator.h | 2 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 7 |
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); |