diff options
Diffstat (limited to 'src/ir/ExpressionAnalyzer.cpp')
-rw-r--r-- | src/ir/ExpressionAnalyzer.cpp | 841 |
1 files changed, 347 insertions, 494 deletions
diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 28c59491a..fecee0cce 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -15,6 +15,9 @@ */ #include "support/hash.h" +#include "support/small_vector.h" +#include "wasm.h" +#include "wasm-traversal.h" #include "ir/iteration.h" #include "ir/load-utils.h" #include "ir/utils.h" @@ -77,527 +80,377 @@ bool ExpressionAnalyzer::isResultDropped(ExpressionStack& stack) { return false; } +// +// Allows visiting the immediate fields of the expression. This is +// useful for comparisons and hashing. +// +// The passed-in visitor object must implement: +// * visitScopeName - a Name that represents a block or loop scope +// * visitNonScopeName - a non-scope name +// * visitInt - anything that has a short enumeration, including +// opcodes, # of bytes in a load, bools, etc. - must be +// guaranteed to fit in an int32 or less. +// * visitLiteral - a Literal +// * visitType - a Type +// * visitIndex - an Index +// * visitAddress - an Address +// -bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, ExprComparer comparer) { - std::vector<Name> nameStack; - std::map<Name, std::vector<Name>> rightNames; // for each name on the left, the stack of names on the right (a stack, since names are scoped and can nest duplicatively - Nop popNameMarker; - std::vector<Expression*> leftStack; - std::vector<Expression*> rightStack; - - auto noteNames = [&](Name left, Name right) { - if (left.is() != right.is()) return false; - if (left.is()) { - nameStack.push_back(left); - rightNames[left].push_back(right); - leftStack.push_back(&popNameMarker); - rightStack.push_back(&popNameMarker); - } - return true; - }; - auto checkNames = [&](Name left, Name right) { - auto iter = rightNames.find(left); - if (iter == rightNames.end()) return left == right; // non-internal name - return iter->second.back() == right; - }; - auto popName = [&]() { - auto left = nameStack.back(); - nameStack.pop_back(); - rightNames[left].pop_back(); - }; +namespace { - leftStack.push_back(left); - rightStack.push_back(right); - - while (leftStack.size() > 0 && rightStack.size() > 0) { - left = leftStack.back(); - leftStack.pop_back(); - right = rightStack.back(); - rightStack.pop_back(); - if (!left != !right) return false; - if (!left) continue; - if (left == &popNameMarker) { - popName(); - continue; - } - if (comparer(left, right)) continue; // comparison hook, before all the rest - // continue with normal structural comparison - if (left->_id != right->_id) return false; - // Compare immediate values - #define CHECK(clazz, what) \ - if (left->cast<clazz>()->what != right->cast<clazz>()->what) return false; - switch (left->_id) { - case Expression::Id::BlockId: { - if (!noteNames(left->cast<Block>()->name, right->cast<Block>()->name)) return false; - CHECK(Block, list.size()); - break; - } - case Expression::Id::LoopId: { - if (!noteNames(left->cast<Loop>()->name, right->cast<Loop>()->name)) return false; - break; - } - case Expression::Id::BreakId: { - if (!checkNames(left->cast<Break>()->name, right->cast<Break>()->name)) return false; - break; - } - case Expression::Id::SwitchId: { - CHECK(Switch, targets.size()); - for (Index i = 0; i < left->cast<Switch>()->targets.size(); i++) { - if (!checkNames(left->cast<Switch>()->targets[i], right->cast<Switch>()->targets[i])) return false; - } - if (!checkNames(left->cast<Switch>()->default_, right->cast<Switch>()->default_)) return false; - break; - } - case Expression::Id::CallId: { - CHECK(Call, target); - CHECK(Call, operands.size()); - break; - } - case Expression::Id::CallIndirectId: { - CHECK(CallIndirect, fullType); - CHECK(CallIndirect, operands.size()); - break; - } - case Expression::Id::GetLocalId: { - CHECK(GetLocal, index); - break; - } - case Expression::Id::SetLocalId: { - CHECK(SetLocal, index); - CHECK(SetLocal, type); // for tee/set - break; - } - case Expression::Id::GetGlobalId: { - CHECK(GetGlobal, name); - break; - } - case Expression::Id::SetGlobalId: { - CHECK(SetGlobal, name); - break; - } - case Expression::Id::LoadId: { - CHECK(Load, bytes); - if (LoadUtils::isSignRelevant(left->cast<Load>()) && - LoadUtils::isSignRelevant(right->cast<Load>())) { - CHECK(Load, signed_); - } - CHECK(Load, offset); - CHECK(Load, align); - CHECK(Load, isAtomic); - break; - } - case Expression::Id::StoreId: { - CHECK(Store, bytes); - CHECK(Store, offset); - CHECK(Store, align); - CHECK(Store, valueType); - CHECK(Store, isAtomic); - break; - } - case Expression::Id::AtomicCmpxchgId: { - CHECK(AtomicCmpxchg, bytes); - CHECK(AtomicCmpxchg, offset); - break; - } - case Expression::Id::AtomicRMWId: { - CHECK(AtomicRMW, op); - CHECK(AtomicRMW, bytes); - CHECK(AtomicRMW, offset); - break; - } - case Expression::Id::AtomicWaitId: { - CHECK(AtomicWait, expectedType); - break; - } - case Expression::Id::AtomicWakeId: { - break; - } - case Expression::Id::SIMDExtractId: { - CHECK(SIMDExtract, op); - CHECK(SIMDExtract, index); - break; - } - case Expression::Id::SIMDReplaceId: { - CHECK(SIMDReplace, op); - CHECK(SIMDReplace, index); - break; - } - case Expression::Id::SIMDShuffleId: { - CHECK(SIMDShuffle, mask); - break; - } - case Expression::Id::SIMDBitselectId: { - break; - } - case Expression::Id::SIMDShiftId: { - CHECK(SIMDShift, op); - break; - } - case Expression::Id::MemoryInitId: { - CHECK(MemoryInit, segment); - break; - } - case Expression::Id::DataDropId: { - CHECK(DataDrop, segment); - break; - } - case Expression::Id::MemoryCopyId: { - break; - } - case Expression::Id::MemoryFillId: { - break; - } - case Expression::Id::ConstId: { - if (left->cast<Const>()->value != right->cast<Const>()->value) { - return false; - } - break; - } - case Expression::Id::UnaryId: { - CHECK(Unary, op); - break; - } - case Expression::Id::BinaryId: { - CHECK(Binary, op); - break; - } - case Expression::Id::HostId: { - CHECK(Host, op); - CHECK(Host, nameOperand); - CHECK(Host, operands.size()); - break; - } - case Expression::Id::NopId: { - break; - } - case Expression::Id::UnreachableId: { - break; - } - case Expression::Id::InvalidId: - case Expression::Id::NumExpressionIds: { - WASM_UNREACHABLE(); +template<typename T> +void visitImmediates(Expression* curr, T& visitor) { + struct ImmediateVisitor : public OverriddenVisitor<ImmediateVisitor> { + T& visitor; + + ImmediateVisitor(Expression* curr, T& visitor) : visitor(visitor) { + this->visit(curr); + } + + void visitBlock(Block* curr) { + visitor.visitScopeName(curr->name); + } + void visitIf(If* curr) { + } + void visitLoop(Loop* curr) { + visitor.visitScopeName(curr->name); + } + void visitBreak(Break* curr) { + visitor.visitScopeName(curr->name); + } + void visitSwitch(Switch* curr) { + for (auto target : curr->targets) { + visitor.visitScopeName(target); } - case Expression::Id::IfId: - case Expression::Id::SelectId: - case Expression::Id::DropId: - case Expression::Id::ReturnId: { - break; // some nodes have no immediate fields + visitor.visitScopeName(curr->default_); + } + void visitCall(Call* curr) { + visitor.visitNonScopeName(curr->target); + } + void visitCallIndirect(CallIndirect* curr) { + visitor.visitNonScopeName(curr->fullType); + } + void visitGetLocal(GetLocal* curr) { + visitor.visitIndex(curr->index); + } + void visitSetLocal(SetLocal* curr) { + visitor.visitIndex(curr->index); + } + void visitGetGlobal(GetGlobal* curr) { + visitor.visitNonScopeName(curr->name); + } + void visitSetGlobal(SetGlobal* curr) { + visitor.visitNonScopeName(curr->name); + } + void visitLoad(Load* curr) { + visitor.visitInt(curr->bytes); + if (curr->type != unreachable && curr->bytes < getTypeSize(curr->type)) { + visitor.visitInt(curr->signed_); + } + visitor.visitAddress(curr->offset); + visitor.visitAddress(curr->align); + visitor.visitInt(curr->isAtomic); + } + void visitStore(Store* curr) { + visitor.visitInt(curr->bytes); + visitor.visitAddress(curr->offset); + visitor.visitAddress(curr->align); + visitor.visitInt(curr->isAtomic); + visitor.visitInt(curr->valueType); + } + void visitAtomicRMW(AtomicRMW* curr) { + visitor.visitInt(curr->op); + visitor.visitInt(curr->bytes); + visitor.visitAddress(curr->offset); + } + void visitAtomicCmpxchg(AtomicCmpxchg* curr) { + visitor.visitInt(curr->bytes); + visitor.visitAddress(curr->offset); + } + void visitAtomicWait(AtomicWait* curr) { + visitor.visitAddress(curr->offset); + visitor.visitType(curr->expectedType); + } + void visitAtomicWake(AtomicWake* curr) { + visitor.visitAddress(curr->offset); + } + void visitSIMDExtract(SIMDExtract* curr) { + visitor.visitInt(curr->op); + visitor.visitInt(curr->index); + } + void visitSIMDReplace(SIMDReplace* curr) { + visitor.visitInt(curr->op); + visitor.visitInt(curr->index); + } + void visitSIMDShuffle(SIMDShuffle* curr) { + for (auto x : curr->mask) { + visitor.visitInt(x); } } - // Add child nodes - for (auto* child : ChildIterator(left)) { - leftStack.push_back(child); + void visitSIMDBitselect(SIMDBitselect* curr) { } - for (auto* child : ChildIterator(right)) { - rightStack.push_back(child); + void visitSIMDShift(SIMDShift* curr) { + visitor.visitInt(curr->op); } - #undef CHECK - } - if (leftStack.size() > 0 || rightStack.size() > 0) return false; - return true; + void visitMemoryInit(MemoryInit* curr) { + visitor.visitIndex(curr->segment); + } + void visitDataDrop(DataDrop* curr) { + visitor.visitIndex(curr->segment); + } + void visitMemoryCopy(MemoryCopy* curr) { + } + void visitMemoryFill(MemoryFill* curr) { + } + void visitConst(Const* curr) { + visitor.visitLiteral(curr->value); + } + void visitUnary(Unary* curr) { + visitor.visitInt(curr->op); + } + void visitBinary(Binary* curr) { + visitor.visitInt(curr->op); + } + void visitSelect(Select* curr) { + } + void visitDrop(Drop* curr) { + } + void visitReturn(Return* curr) { + } + void visitHost(Host* curr) { + visitor.visitInt(curr->op); + visitor.visitNonScopeName(curr->nameOperand); + } + void visitNop(Nop* curr) { + } + void visitUnreachable(Unreachable* curr) { + } + } singleton(curr, visitor); } +} // namespace -// hash an expression, ignoring superficial details like specific internal names -HashType ExpressionAnalyzer::hash(Expression* curr) { - HashType digest = 0; +bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, ExprComparer comparer) { + struct Comparer { + std::map<Name, Name> rightNames; // for each name on the left, the corresponding name on the right + std::vector<Expression*> leftStack; + std::vector<Expression*> rightStack; - auto hash = [&digest](HashType hash) { - digest = rehash(digest, hash); - }; - auto hash64 = [&digest](uint64_t hash) { - digest = rehash(rehash(digest, HashType(hash >> 32)), HashType(hash)); - }; + struct Immediates { + Comparer& parent; - std::vector<Name> nameStack; - Index internalCounter = 0; - std::map<Name, std::vector<Index>> internalNames; // for each internal name, a vector if unique ids - Nop popNameMarker; - std::vector<Expression*> stack; + Immediates(Comparer& parent) : parent(parent) {} - auto noteName = [&](Name curr) { - if (curr.is()) { - nameStack.push_back(curr); - internalNames[curr].push_back(internalCounter++); - stack.push_back(&popNameMarker); - } - return true; - }; - auto hashName = [&](Name curr) { - auto iter = internalNames.find(curr); - if (iter == internalNames.end()) hash64(uint64_t(curr.str)); - else hash(iter->second.back()); - }; - auto popName = [&]() { - auto curr = nameStack.back(); - nameStack.pop_back(); - internalNames[curr].pop_back(); - }; + SmallVector<Name, 1> scopeNames; + SmallVector<Name, 1> nonScopeNames; + SmallVector<int32_t, 3> ints; + SmallVector<Literal, 1> literals; + SmallVector<Type, 1> types; + SmallVector<Index, 1> indexes; + SmallVector<Address, 2> addresses; - stack.push_back(curr); - - while (stack.size() > 0) { - curr = stack.back(); - stack.pop_back(); - if (!curr) continue; - if (curr == &popNameMarker) { - popName(); - continue; - } - hash(curr->_id); - // we often don't need to hash the type, as it is tied to other values - // we are hashing anyhow, but there are exceptions: for example, a - // local.get's type is determined by the function, so if we are - // hashing only expression fragments, then two from different - // functions may turn out the same even if the type differs. Likewise, - // if we hash between modules, then we need to take int account - // call_imports type, etc. The simplest thing is just to hash the - // type for all of them. - hash(curr->type); - - #define PUSH(clazz, what) \ - stack.push_back(curr->cast<clazz>()->what); - #define HASH(clazz, what) \ - hash(curr->cast<clazz>()->what); - #define HASH64(clazz, what) \ - hash64(curr->cast<clazz>()->what); - #define HASH_NAME(clazz, what) \ - hash64(uint64_t(curr->cast<clazz>()->what.str)); - #define HASH_PTR(clazz, what) \ - hash64(uint64_t(curr->cast<clazz>()->what)); - switch (curr->_id) { - case Expression::Id::BlockId: { - noteName(curr->cast<Block>()->name); - HASH(Block, list.size()); - for (Index i = 0; i < curr->cast<Block>()->list.size(); i++) { - PUSH(Block, list[i]); + void visitScopeName(Name curr) { scopeNames.push_back(curr); } + void visitNonScopeName(Name curr) { nonScopeNames.push_back(curr); } + void visitInt(int32_t curr) { ints.push_back(curr); } + void visitLiteral(Literal curr) { literals.push_back(curr); } + void visitType(Type curr) { types.push_back(curr); } + void visitIndex(Index curr) { indexes.push_back(curr); } + void visitAddress(Address curr) { addresses.push_back(curr); } + + // Comparison is by value, except for names, which must match. + bool operator==(const Immediates& other) { + if (scopeNames.size() != other.scopeNames.size()) return false; + for (Index i = 0; i < scopeNames.size(); i++) { + auto leftName = scopeNames[i]; + auto rightName = other.scopeNames[i]; + auto iter = parent.rightNames.find(leftName); + // If it's not found, that means it was defined out of the expression being compared, + // in which case we can just treat it literally - it must be exactly identical. + if (iter != parent.rightNames.end()) { + leftName = iter->second; + } + if (leftName != rightName) { + return false; + } } - break; - } - case Expression::Id::IfId: { - PUSH(If, condition); - PUSH(If, ifTrue); - PUSH(If, ifFalse); - break; + if (nonScopeNames != other.nonScopeNames) return false; + if (ints != other.ints) return false; + if (literals != other.literals) return false; + if (types != other.types) return false; + if (indexes != other.indexes) return false; + if (addresses != other.addresses) return false; + return true; } - case Expression::Id::LoopId: { - noteName(curr->cast<Loop>()->name); - PUSH(Loop, body); - break; - } - case Expression::Id::BreakId: { - hashName(curr->cast<Break>()->name); - PUSH(Break, condition); - PUSH(Break, value); - break; + + bool operator!=(const Immediates& other) { + return !(*this == other); } - case Expression::Id::SwitchId: { - HASH(Switch, targets.size()); - for (Index i = 0; i < curr->cast<Switch>()->targets.size(); i++) { - hashName(curr->cast<Switch>()->targets[i]); - } - hashName(curr->cast<Switch>()->default_); - PUSH(Switch, condition); - PUSH(Switch, value); - break; + + void clear() { + scopeNames.clear(); + nonScopeNames.clear(); + ints.clear(); + literals.clear(); + types.clear(); + indexes.clear(); + addresses.clear(); + } + }; + + bool noteNames(Name left, Name right) { + if (left.is() != right.is()) return false; + if (left.is()) { + assert(rightNames.find(left) == rightNames.end()); + rightNames[left] = right; } - case Expression::Id::CallId: { - HASH_NAME(Call, target); - HASH(Call, operands.size()); - for (Index i = 0; i < curr->cast<Call>()->operands.size(); i++) { - PUSH(Call, operands[i]); + return true; + } + + bool compare(Expression* left, Expression* right, ExprComparer comparer) { + Immediates leftImmediates(*this), + rightImmediates(*this); + + // The empty name is the same on both sides. + rightNames[Name()] = Name(); + + leftStack.push_back(left); + rightStack.push_back(right); + + while (leftStack.size() > 0 && rightStack.size() > 0) { + left = leftStack.back(); + leftStack.pop_back(); + right = rightStack.back(); + rightStack.pop_back(); + if (!left != !right) return false; + if (!left) continue; + if (comparer(left, right)) continue; // comparison hook, before all the rest + // continue with normal structural comparison + if (left->_id != right->_id) return false; + // Blocks and loops introduce scoping. + if (auto* block = left->dynCast<Block>()) { + if (!noteNames(block->name, right->cast<Block>()->name)) return false; + } else if (auto* loop = left->dynCast<Loop>()) { + if (!noteNames(loop->name, right->cast<Loop>()->name)) return false; + } else { + // For all other nodes, compare their immediate values + visitImmediates(left, leftImmediates); + visitImmediates(right, rightImmediates); + if (leftImmediates != rightImmediates) return false; + leftImmediates.clear(); + rightImmediates.clear(); } - break; - } - case Expression::Id::CallIndirectId: { - PUSH(CallIndirect, target); - HASH_NAME(CallIndirect, fullType); - HASH(CallIndirect, operands.size()); - for (Index i = 0; i < curr->cast<CallIndirect>()->operands.size(); i++) { - PUSH(CallIndirect, operands[i]); + // Add child nodes. + Index counter = 0; + for (auto* child : ChildIterator(left)) { + leftStack.push_back(child); + counter++; } - break; - } - case Expression::Id::GetLocalId: { - HASH(GetLocal, index); - break; - } - case Expression::Id::SetLocalId: { - HASH(SetLocal, index); - PUSH(SetLocal, value); - break; - } - case Expression::Id::GetGlobalId: { - HASH_NAME(GetGlobal, name); - break; - } - case Expression::Id::SetGlobalId: { - HASH_NAME(SetGlobal, name); - PUSH(SetGlobal, value); - break; - } - case Expression::Id::LoadId: { - HASH(Load, bytes); - if (LoadUtils::isSignRelevant(curr->cast<Load>())) { - HASH(Load, signed_); + for (auto* child : ChildIterator(right)) { + rightStack.push_back(child); + counter--; } - HASH(Load, offset); - HASH(Load, align); - HASH(Load, isAtomic); - PUSH(Load, ptr); - break; - } - case Expression::Id::StoreId: { - HASH(Store, bytes); - HASH(Store, offset); - HASH(Store, align); - HASH(Store, valueType); - HASH(Store, isAtomic); - PUSH(Store, ptr); - PUSH(Store, value); - break; - } - case Expression::Id::AtomicCmpxchgId: { - HASH(AtomicCmpxchg, bytes); - HASH(AtomicCmpxchg, offset); - PUSH(AtomicCmpxchg, ptr); - PUSH(AtomicCmpxchg, expected); - PUSH(AtomicCmpxchg, replacement); - break; + // The number of child nodes must match (e.g. return has an optional one). + if (counter != 0) return false; } - case Expression::Id::AtomicRMWId: { - HASH(AtomicRMW, op); - HASH(AtomicRMW, bytes); - HASH(AtomicRMW, offset); - PUSH(AtomicRMW, ptr); - PUSH(AtomicRMW, value); - break; - } - case Expression::Id::AtomicWaitId: { - HASH(AtomicWait, offset); - HASH(AtomicWait, expectedType); - PUSH(AtomicWait, ptr); - PUSH(AtomicWait, expected); - PUSH(AtomicWait, timeout); - break; - } - case Expression::Id::AtomicWakeId: { - HASH(AtomicWake, offset); - PUSH(AtomicWake, ptr); - PUSH(AtomicWake, wakeCount); - break; - } - case Expression::Id::SIMDExtractId: { - HASH(SIMDExtract, op); - HASH(SIMDExtract, index); - PUSH(SIMDExtract, vec); - break; - } - case Expression::Id::SIMDReplaceId: { - HASH(SIMDReplace, op); - HASH(SIMDReplace, index); - PUSH(SIMDReplace, vec); - PUSH(SIMDReplace, value); - break; + if (leftStack.size() > 0 || rightStack.size() > 0) return false; + return true; + } + }; + + return Comparer().compare(left, right, comparer); +} + +// hash an expression, ignoring superficial details like specific internal names +HashType ExpressionAnalyzer::hash(Expression* curr) { + struct Hasher { + HashType digest = 0; + + Index internalCounter = 0; + std::map<Name, Index> internalNames; // for each internal name, its unique id + ExpressionStack stack; + + void noteScopeName(Name curr) { + if (curr.is()) { + internalNames[curr] = internalCounter++; } - case Expression::Id::SIMDShuffleId: { - for (size_t i = 0; i < 16; ++i) { - HASH(SIMDShuffle, mask[i]); + } + + Hasher(Expression* curr) { + stack.push_back(curr); + + while (stack.size() > 0) { + curr = stack.back(); + stack.pop_back(); + if (!curr) continue; + hash(curr->_id); + // we often don't need to hash the type, as it is tied to other values + // we are hashing anyhow, but there are exceptions: for example, a + // local.get's type is determined by the function, so if we are + // hashing only expression fragments, then two from different + // functions may turn out the same even if the type differs. Likewise, + // if we hash between modules, then we need to take int account + // call_imports type, etc. The simplest thing is just to hash the + // type for all of them. + hash(curr->type); + // Blocks and loops introduce scoping. + if (auto* block = curr->dynCast<Block>()) { + noteScopeName(block->name); + } else if (auto* loop = curr->dynCast<Loop>()) { + noteScopeName(loop->name); + } else { + // For all other nodes, compare their immediate values + visitImmediates(curr, *this); } - PUSH(SIMDShuffle, left); - PUSH(SIMDShuffle, right); - break; - } - case Expression::Id::SIMDBitselectId: { - PUSH(SIMDBitselect, left); - PUSH(SIMDBitselect, right); - PUSH(SIMDBitselect, cond); - break; - } - case Expression::Id::SIMDShiftId: { - HASH(SIMDShift, op); - PUSH(SIMDShift, vec); - PUSH(SIMDShift, shift); - break; - } - case Expression::Id::MemoryInitId: { - HASH(MemoryInit, segment); - PUSH(MemoryInit, dest); - PUSH(MemoryInit, offset); - PUSH(MemoryInit, size); - break; - } - case Expression::Id::DataDropId: { - HASH(DataDrop, segment); - break; - } - case Expression::Id::MemoryCopyId: { - PUSH(MemoryCopy, dest); - PUSH(MemoryCopy, source); - PUSH(MemoryCopy, size); - break; - } - case Expression::Id::MemoryFillId: { - PUSH(MemoryFill, dest); - PUSH(MemoryFill, value); - PUSH(MemoryFill, size); - break; - } - case Expression::Id::ConstId: { - auto* c = curr->cast<Const>(); - hash(c->type); - hash(std::hash<Literal>()(c->value)); - break; - } - case Expression::Id::UnaryId: { - HASH(Unary, op); - PUSH(Unary, value); - break; - } - case Expression::Id::BinaryId: { - HASH(Binary, op); - PUSH(Binary, left); - PUSH(Binary, right); - break; - } - case Expression::Id::SelectId: { - PUSH(Select, ifTrue); - PUSH(Select, ifFalse); - PUSH(Select, condition); - break; - } - case Expression::Id::DropId: { - PUSH(Drop, value); - break; - } - case Expression::Id::ReturnId: { - PUSH(Return, value); - break; - } - case Expression::Id::HostId: { - HASH(Host, op); - HASH_NAME(Host, nameOperand); - HASH(Host, operands.size()); - for (Index i = 0; i < curr->cast<Host>()->operands.size(); i++) { - PUSH(Host, operands[i]); + // Hash children + Index counter = 0; + for (auto* child : ChildIterator(curr)) { + stack.push_back(child); + counter++; } - break; - } - case Expression::Id::NopId: { - break; - } - case Expression::Id::UnreachableId: { - break; - } - case Expression::Id::InvalidId: - case Expression::Id::NumExpressionIds: { - WASM_UNREACHABLE(); + // Sometimes children are optional, e.g. return, so we must hash + // their number as well. + hash(counter); } } - #undef HASH - #undef PUSH - } - return digest; + + void hash(HashType hash) { + digest = rehash(digest, hash); + } + void hash64(uint64_t hash) { + digest = rehash(rehash(digest, HashType(hash >> 32)), HashType(hash)); + } + + void visitScopeName(Name curr) { + // Names are relative, we give the same hash for + // (block $x (br $x)) + // (block $y (br $y)) + static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); + assert(internalNames.find(curr) != internalNames.end()); + return hash(internalNames[curr]); + } + void visitNonScopeName(Name curr) { + return hash64(uint64_t(curr.str)); + } + void visitInt(int32_t curr) { + hash(curr); + } + void visitLiteral(Literal curr) { + hash(std::hash<Literal>()(curr)); + } + void visitType(Type curr) { + hash(int32_t(curr)); + } + void visitIndex(Index curr) { + static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); + hash(int32_t(curr)); + } + void visitAddress(Address curr) { + static_assert(sizeof(Address) == sizeof(int32_t), "wasm64 will need changes here"); + hash(int32_t(curr)); + } + }; + + return Hasher(curr).digest; } } // namespace wasm |