diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/cost.h | 54 | ||||
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 10 |
2 files changed, 37 insertions, 27 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h index 6622561b8..38c74a203 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -31,11 +31,17 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType cost; - // A cost that is extremely high, something that is far, far more expensive - // than a fast operation like an add. This cost is so high it is unacceptable - // to add any more of it, say by an If=>Select operation (which would execute - // both arms; if either arm contains an unacceptable cost, we do not do it). - static const CostType Unacceptable = 100; + // The cost of a "slow" atomic operation like RMW. + static const CostType AtomicCost = 10; + + // The cost of throwing a wasm exception. This does not include the cost of + // catching it (which might be in another function than the one we are + // considering). + static const CostType ThrowCost = 10; + + // The cost of a cast. This can vary depending on the engine and on the type, + // but usually requires some loads and comparisons. + static const CostType CastCost = 5; CostType maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; } @@ -85,26 +91,27 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitGlobalGet(GlobalGet* curr) { return 1; } CostType visitGlobalSet(GlobalSet* curr) { return 2 + visit(curr->value); } CostType visitLoad(Load* curr) { - return 1 + visit(curr->ptr) + 10 * curr->isAtomic; + return 1 + visit(curr->ptr) + AtomicCost * curr->isAtomic; } CostType visitStore(Store* curr) { - return 2 + visit(curr->ptr) + visit(curr->value) + 10 * curr->isAtomic; + return 2 + visit(curr->ptr) + visit(curr->value) + + AtomicCost * curr->isAtomic; } CostType visitAtomicRMW(AtomicRMW* curr) { - return Unacceptable + visit(curr->ptr) + visit(curr->value); + return AtomicCost + visit(curr->ptr) + visit(curr->value); } CostType visitAtomicCmpxchg(AtomicCmpxchg* curr) { - return Unacceptable + visit(curr->ptr) + visit(curr->expected) + + return AtomicCost + visit(curr->ptr) + visit(curr->expected) + visit(curr->replacement); } CostType visitAtomicWait(AtomicWait* curr) { - return Unacceptable + visit(curr->ptr) + visit(curr->expected) + + return AtomicCost + visit(curr->ptr) + visit(curr->expected) + visit(curr->timeout); } CostType visitAtomicNotify(AtomicNotify* curr) { - return Unacceptable + visit(curr->ptr) + visit(curr->notifyCount); + return AtomicCost + visit(curr->ptr) + visit(curr->notifyCount); } - CostType visitAtomicFence(AtomicFence* curr) { return Unacceptable; } + CostType visitAtomicFence(AtomicFence* curr) { return AtomicCost; } CostType visitConst(Const* curr) { return 1; } CostType visitUnary(Unary* curr) { CostType ret = 0; @@ -516,7 +523,8 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitReturn(Return* curr) { return maybeVisit(curr->value); } CostType visitMemorySize(MemorySize* curr) { return 1; } CostType visitMemoryGrow(MemoryGrow* curr) { - return Unacceptable + visit(curr->delta); + // TODO: This should perhaps be higher for shared memories. + return 20 + visit(curr->delta); } CostType visitMemoryInit(MemoryInit* curr) { return 6 + visit(curr->dest) + visit(curr->offset) + visit(curr->size); @@ -572,7 +580,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { } CostType visitTableSize(TableSize* curr) { return 1; } CostType visitTableGrow(TableGrow* curr) { - return Unacceptable + visit(curr->value) + visit(curr->delta); + return 20 + visit(curr->value) + visit(curr->delta); } CostType visitTableFill(TableFill* curr) { return 6 + visit(curr->dest) + visit(curr->value) + visit(curr->size); @@ -589,14 +597,14 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { return visit(curr->body); } CostType visitThrow(Throw* curr) { - CostType ret = Unacceptable; + CostType ret = ThrowCost; for (auto* child : curr->operands) { ret += visit(child); } return ret; } - CostType visitRethrow(Rethrow* curr) { return Unacceptable; } - CostType visitThrowRef(ThrowRef* curr) { return Unacceptable; } + CostType visitRethrow(Rethrow* curr) { return ThrowCost; } + CostType visitThrowRef(ThrowRef* curr) { return ThrowCost; } CostType visitTupleMake(TupleMake* curr) { CostType ret = 0; for (auto* child : curr->operands) { @@ -612,19 +620,15 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitRefI31(RefI31* curr) { return 3 + visit(curr->value); } CostType visitI31Get(I31Get* curr) { return 2 + visit(curr->i31); } CostType visitRefTest(RefTest* curr) { - // Casts have a very high cost because in the VM they end up implemented as - // a combination of loads and branches. Given they contain branches, we do - // not want to add any more such work. - return Unacceptable + nullCheckCost(curr->ref) + visit(curr->ref); + return CastCost + nullCheckCost(curr->ref) + visit(curr->ref); } CostType visitRefCast(RefCast* curr) { - return Unacceptable + nullCheckCost(curr->ref) + visit(curr->ref); + return CastCost + nullCheckCost(curr->ref) + visit(curr->ref); } CostType visitBrOn(BrOn* curr) { - // BrOn of a null can be fairly fast, but anything else is a cast check - // basically, and an unacceptable cost. + // BrOn of a null can be fairly fast, but anything else is a cast check. CostType base = - curr->op == BrOnNull || curr->op == BrOnNonNull ? 2 : Unacceptable; + curr->op == BrOnNull || curr->op == BrOnNonNull ? 2 : CastCost; return base + nullCheckCost(curr->ref) + maybeVisit(curr->ref); } CostType visitStructNew(StructNew* curr) { diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 893cf9039..fcb4ea56a 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -88,8 +88,14 @@ static bool canTurnIfIntoBrIf(Expression* ifCondition, // * https://github.com/WebAssembly/binaryen/issues/5983 const Index TooCostlyToRunUnconditionally = 8; -static_assert(TooCostlyToRunUnconditionally < CostAnalyzer::Unacceptable, - "We never run code unconditionally if it has unacceptable cost"); +// Some costs are known to be too high to move from conditional to unconditional +// execution. +static_assert(CostAnalyzer::AtomicCost >= TooCostlyToRunUnconditionally, + "We never run atomics unconditionally"); +static_assert(CostAnalyzer::ThrowCost >= TooCostlyToRunUnconditionally, + "We never run throws unconditionally"); +static_assert(CostAnalyzer::CastCost > TooCostlyToRunUnconditionally / 2, + "We only run casts unconditionally when optimizing for size"); static bool tooCostlyToRunUnconditionally(const PassOptions& passOptions, Index cost) { |