diff options
-rw-r--r-- | src/ir/cost.h | 168 |
1 files changed, 86 insertions, 82 deletions
diff --git a/src/ir/cost.h b/src/ir/cost.h index 434d88d60..63424c8b3 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -24,82 +24,84 @@ namespace wasm { // Measure the execution cost of an AST. Very handwave-ey -struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { +using CostType = uint32_t; + +struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostAnalyzer(Expression* ast) { cost = visit(ast); } - Index cost; + CostType cost; - Index maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; } + CostType maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; } - Index visitBlock(Block* curr) { - Index ret = 0; + CostType visitBlock(Block* curr) { + CostType ret = 0; for (auto* child : curr->list) { ret += visit(child); } return ret; } - Index visitIf(If* curr) { + CostType visitIf(If* curr) { return 1 + visit(curr->condition) + std::max(visit(curr->ifTrue), maybeVisit(curr->ifFalse)); } - Index visitLoop(Loop* curr) { return 5 * visit(curr->body); } - Index visitBreak(Break* curr) { + CostType visitLoop(Loop* curr) { return 5 * visit(curr->body); } + CostType visitBreak(Break* curr) { return 1 + maybeVisit(curr->value) + maybeVisit(curr->condition); } - Index visitSwitch(Switch* curr) { + CostType visitSwitch(Switch* curr) { return 2 + visit(curr->condition) + maybeVisit(curr->value); } - Index visitCall(Call* curr) { + CostType visitCall(Call* curr) { // XXX this does not take into account if the call is to an import, which // may be costlier in general - Index ret = 4; + CostType ret = 4; for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitCallIndirect(CallIndirect* curr) { - Index ret = 6 + visit(curr->target); + CostType visitCallIndirect(CallIndirect* curr) { + CostType ret = 6 + visit(curr->target); for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitCallRef(CallRef* curr) { - Index ret = 5 + visit(curr->target); + CostType visitCallRef(CallRef* curr) { + CostType ret = 5 + visit(curr->target); for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitLocalGet(LocalGet* curr) { return 0; } - Index visitLocalSet(LocalSet* curr) { return 1 + visit(curr->value); } - Index visitGlobalGet(GlobalGet* curr) { return 1; } - Index visitGlobalSet(GlobalSet* curr) { return 2 + visit(curr->value); } - Index visitLoad(Load* curr) { + CostType visitLocalGet(LocalGet* curr) { return 0; } + CostType visitLocalSet(LocalSet* curr) { return 1 + visit(curr->value); } + 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; } - Index visitStore(Store* curr) { + CostType visitStore(Store* curr) { return 2 + visit(curr->ptr) + visit(curr->value) + 10 * curr->isAtomic; } - Index visitAtomicRMW(AtomicRMW* curr) { + CostType visitAtomicRMW(AtomicRMW* curr) { return 100 + visit(curr->ptr) + visit(curr->value); } - Index visitAtomicCmpxchg(AtomicCmpxchg* curr) { + CostType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return 100 + visit(curr->ptr) + visit(curr->expected) + visit(curr->replacement); } - Index visitAtomicWait(AtomicWait* curr) { + CostType visitAtomicWait(AtomicWait* curr) { return 100 + visit(curr->ptr) + visit(curr->expected) + visit(curr->timeout); } - Index visitAtomicNotify(AtomicNotify* curr) { + CostType visitAtomicNotify(AtomicNotify* curr) { return 100 + visit(curr->ptr) + visit(curr->notifyCount); } - Index visitAtomicFence(AtomicFence* curr) { return 100; } - Index visitConst(Const* curr) { return 1; } - Index visitUnary(Unary* curr) { - Index ret = 0; + CostType visitAtomicFence(AtomicFence* curr) { return 100; } + CostType visitConst(Const* curr) { return 1; } + CostType visitUnary(Unary* curr) { + CostType ret = 0; switch (curr->op) { case ClzInt32: case CtzInt32: @@ -237,8 +239,8 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } return ret + visit(curr->value); } - Index visitBinary(Binary* curr) { - Index ret = 0; + CostType visitBinary(Binary* curr) { + CostType ret = 0; switch (curr->op) { case AddInt32: case SubInt32: @@ -489,34 +491,36 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } return ret + visit(curr->left) + visit(curr->right); } - Index visitSelect(Select* curr) { + CostType visitSelect(Select* curr) { return 1 + visit(curr->condition) + visit(curr->ifTrue) + visit(curr->ifFalse); } - Index visitDrop(Drop* curr) { return visit(curr->value); } - Index visitReturn(Return* curr) { return maybeVisit(curr->value); } - Index visitMemorySize(MemorySize* curr) { return 1; } - Index visitMemoryGrow(MemoryGrow* curr) { return 100 + visit(curr->delta); } - Index visitMemoryInit(MemoryInit* curr) { + CostType visitDrop(Drop* curr) { return visit(curr->value); } + CostType visitReturn(Return* curr) { return maybeVisit(curr->value); } + CostType visitMemorySize(MemorySize* curr) { return 1; } + CostType visitMemoryGrow(MemoryGrow* curr) { + return 100 + visit(curr->delta); + } + CostType visitMemoryInit(MemoryInit* curr) { return 6 + visit(curr->dest) + visit(curr->offset) + visit(curr->size); } - Index visitMemoryCopy(MemoryCopy* curr) { + CostType visitMemoryCopy(MemoryCopy* curr) { // TODO when the size is a constant, estimate the time based on that return 6 + visit(curr->dest) + visit(curr->source) + visit(curr->size); } - Index visitMemoryFill(MemoryFill* curr) { + CostType visitMemoryFill(MemoryFill* curr) { return 6 + visit(curr->dest) + visit(curr->value) + visit(curr->size); } - Index visitSIMDLoad(SIMDLoad* curr) { return 1 + visit(curr->ptr); } - Index visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { - return 1 + Index(curr->isStore()) + visit(curr->ptr) + visit(curr->vec); + CostType visitSIMDLoad(SIMDLoad* curr) { return 1 + visit(curr->ptr); } + CostType visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { + return 1 + CostType(curr->isStore()) + visit(curr->ptr) + visit(curr->vec); } - Index visitSIMDReplace(SIMDReplace* curr) { + CostType visitSIMDReplace(SIMDReplace* curr) { return 2 + visit(curr->vec) + visit(curr->value); } - Index visitSIMDExtract(SIMDExtract* curr) { return 1 + visit(curr->vec); } - Index visitSIMDTernary(SIMDTernary* curr) { - Index ret = 0; + CostType visitSIMDExtract(SIMDExtract* curr) { return 1 + visit(curr->vec); } + CostType visitSIMDTernary(SIMDTernary* curr) { + CostType ret = 0; switch (curr->op) { case Bitselect: ret = 1; @@ -524,103 +528,103 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } return ret + visit(curr->a) + visit(curr->b) + visit(curr->c); } - Index visitSIMDShift(SIMDShift* curr) { + CostType visitSIMDShift(SIMDShift* curr) { return 1 + visit(curr->vec) + visit(curr->shift); } - Index visitSIMDShuffle(SIMDShuffle* curr) { + CostType visitSIMDShuffle(SIMDShuffle* curr) { return 1 + visit(curr->left) + visit(curr->right); } - Index visitRefNull(RefNull* curr) { return 1; } - Index visitRefIs(RefIs* curr) { return 1 + visit(curr->value); } - Index visitRefFunc(RefFunc* curr) { return 1; } - Index visitRefEq(RefEq* curr) { + CostType visitRefNull(RefNull* curr) { return 1; } + CostType visitRefIs(RefIs* curr) { return 1 + visit(curr->value); } + CostType visitRefFunc(RefFunc* curr) { return 1; } + CostType visitRefEq(RefEq* curr) { return 1 + visit(curr->left) + visit(curr->right); } - Index visitTry(Try* curr) { + CostType visitTry(Try* curr) { // We assume no exception will be thrown in most cases return visit(curr->body); } - Index visitThrow(Throw* curr) { - Index ret = 100; + CostType visitThrow(Throw* curr) { + CostType ret = 100; for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitRethrow(Rethrow* curr) { return 100; } - Index visitTupleMake(TupleMake* curr) { - Index ret = 0; + CostType visitRethrow(Rethrow* curr) { return 100; } + CostType visitTupleMake(TupleMake* curr) { + CostType ret = 0; for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitTupleExtract(TupleExtract* curr) { return visit(curr->tuple); } - Index visitPop(Pop* curr) { return 0; } - Index visitNop(Nop* curr) { return 0; } - Index visitUnreachable(Unreachable* curr) { return 0; } - Index visitDataDrop(DataDrop* curr) { return 5; } - Index visitI31New(I31New* curr) { return 3 + visit(curr->value); } - Index visitI31Get(I31Get* curr) { return 2 + visit(curr->i31); } - Index visitRefTest(RefTest* curr) { + CostType visitTupleExtract(TupleExtract* curr) { return visit(curr->tuple); } + CostType visitPop(Pop* curr) { return 0; } + CostType visitNop(Nop* curr) { return 0; } + CostType visitUnreachable(Unreachable* curr) { return 0; } + CostType visitDataDrop(DataDrop* curr) { return 5; } + CostType visitI31New(I31New* curr) { return 3 + visit(curr->value); } + CostType visitI31Get(I31Get* curr) { return 2 + visit(curr->i31); } + CostType visitRefTest(RefTest* curr) { return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt); } - Index visitRefCast(RefCast* curr) { + CostType visitRefCast(RefCast* curr) { return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->rtt); } - Index visitBrOn(BrOn* curr) { + CostType visitBrOn(BrOn* curr) { // BrOnCast has more work to do with the rtt, so add a little there. - Index base = curr->op == BrOnCast ? 3 : 2; + CostType base = curr->op == BrOnCast ? 3 : 2; return base + nullCheckCost(curr->ref) + visit(curr->ref) + maybeVisit(curr->rtt); } - Index visitRttCanon(RttCanon* curr) { + CostType visitRttCanon(RttCanon* curr) { // TODO: investigate actual RTT costs in VMs return 1; } - Index visitRttSub(RttSub* curr) { + CostType visitRttSub(RttSub* curr) { // TODO: investigate actual RTT costs in VMs return 2 + visit(curr->parent); } - Index visitStructNew(StructNew* curr) { + CostType visitStructNew(StructNew* curr) { // While allocation itself is almost free with generational GC, there is // at least some baseline cost, plus writing the fields. (If we use default // values for the fields, then it is possible they are all 0 and if so, we // can get that almost for free as well, so don't add anything there.) - Index ret = 4 + visit(curr->rtt) + curr->operands.size(); + CostType ret = 4 + visit(curr->rtt) + curr->operands.size(); for (auto* child : curr->operands) { ret += visit(child); } return ret; } - Index visitStructGet(StructGet* curr) { + CostType visitStructGet(StructGet* curr) { return 1 + nullCheckCost(curr->ref) + visit(curr->ref); } - Index visitStructSet(StructSet* curr) { + CostType visitStructSet(StructSet* curr) { return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->value); } - Index visitArrayNew(ArrayNew* curr) { + CostType visitArrayNew(ArrayNew* curr) { return 4 + visit(curr->rtt) + visit(curr->size) + maybeVisit(curr->init); } - Index visitArrayGet(ArrayGet* curr) { + CostType visitArrayGet(ArrayGet* curr) { return 1 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->index); } - Index visitArraySet(ArraySet* curr) { + CostType visitArraySet(ArraySet* curr) { return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->index) + visit(curr->value); } - Index visitArrayLen(ArrayLen* curr) { + CostType visitArrayLen(ArrayLen* curr) { return 1 + nullCheckCost(curr->ref) + visit(curr->ref); } - Index visitArrayCopy(ArrayCopy* curr) { + CostType visitArrayCopy(ArrayCopy* curr) { // Similar to MemoryCopy. return 6 + visit(curr->destRef) + visit(curr->destIndex) + visit(curr->srcRef) + visit(curr->srcIndex) + visit(curr->length); } - Index visitRefAs(RefAs* curr) { return 1 + visit(curr->value); } + CostType visitRefAs(RefAs* curr) { return 1 + visit(curr->value); } private: - Index nullCheckCost(Expression* ref) { + CostType nullCheckCost(Expression* ref) { // A nullable type requires a bounds check in most VMs. return ref->type.isNullable(); } |