#include "ir/iteration.h"
#include "ir/load-utils.h"
#include "ir/utils.h"
#include "support/hash.h"
#include "support/small_vector.h"
#include "wasm-traversal.h"
#include "wasm.h"
namespace wasm {
bool ExpressionAnalyzer::isResultUsed(ExpressionStack& stack, Function* func) {
for (int i = int(stack.size()) - 2; i >= 0; i--) {
auto* curr = stack[i];
auto* above = stack[i + 1];
if (curr->is<Block>()) {
auto* block = curr->cast<Block>();
for (size_t j = 0; j < block->list.size() - 1; j++) {
if (block->list[j] == above) {
return false;
}
}
assert(block->list.back() == above);
} else if (curr->is<If>()) {
auto* iff = curr->cast<If>();
if (above == iff->condition) {
return true;
}
if (!iff->ifFalse) {
return false;
}
assert(above == iff->ifTrue || above == iff->ifFalse);
} else {
if (curr->is<Drop>()) {
return false;
}
return true; }
}
return func->sig.results != Type::none;
}
bool ExpressionAnalyzer::isResultDropped(ExpressionStack& stack) {
for (int i = int(stack.size()) - 2; i >= 0; i--) {
auto* curr = stack[i];
auto* above = stack[i + 1];
if (curr->is<Block>()) {
auto* block = curr->cast<Block>();
for (size_t j = 0; j < block->list.size() - 1; j++) {
if (block->list[j] == above) {
return false;
}
}
assert(block->list.back() == above);
} else if (curr->is<If>()) {
auto* iff = curr->cast<If>();
if (above == iff->condition) {
return false;
}
if (!iff->ifFalse) {
return false;
}
assert(above == iff->ifTrue || above == iff->ifFalse);
} else {
if (curr->is<Drop>()) {
return true; }
return false; }
}
return false;
}
namespace {
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);
}
visitor.visitScopeName(curr->default_);
}
void visitCall(Call* curr) {
visitor.visitNonScopeName(curr->target);
visitor.visitInt(curr->isReturn);
}
void visitCallIndirect(CallIndirect* curr) {
visitor.visitInt(curr->sig.params.getID());
visitor.visitInt(curr->sig.results.getID());
visitor.visitInt(curr->isReturn);
}
void visitLocalGet(LocalGet* curr) { visitor.visitIndex(curr->index); }
void visitLocalSet(LocalSet* curr) { visitor.visitIndex(curr->index); }
void visitGlobalGet(GlobalGet* curr) {
visitor.visitNonScopeName(curr->name);
}
void visitGlobalSet(GlobalSet* curr) {
visitor.visitNonScopeName(curr->name);
}
void visitLoad(Load* curr) {
visitor.visitInt(curr->bytes);
if (curr->type != Type::unreachable &&
curr->bytes < curr->type.getByteSize()) {
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.getID());
}
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 visitAtomicNotify(AtomicNotify* curr) {
visitor.visitAddress(curr->offset);
}
void visitAtomicFence(AtomicFence* curr) { visitor.visitInt(curr->order); }
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);
}
}
void visitSIMDTernary(SIMDTernary* curr) { visitor.visitInt(curr->op); }
void visitSIMDShift(SIMDShift* curr) { visitor.visitInt(curr->op); }
void visitSIMDLoad(SIMDLoad* curr) {
visitor.visitInt(curr->op);
visitor.visitAddress(curr->offset);
visitor.visitAddress(curr->align);
}
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 visitMemorySize(MemorySize* curr) {}
void visitMemoryGrow(MemoryGrow* curr) {}
void visitRefNull(RefNull* curr) { visitor.visitType(curr->type); }
void visitRefIsNull(RefIsNull* curr) {}
void visitRefFunc(RefFunc* curr) { visitor.visitNonScopeName(curr->func); }
void visitRefEq(RefEq* curr) {}
void visitTry(Try* curr) {}
void visitThrow(Throw* curr) { visitor.visitNonScopeName(curr->event); }
void visitRethrow(Rethrow* curr) {}
void visitBrOnExn(BrOnExn* curr) {
visitor.visitScopeName(curr->name);
visitor.visitNonScopeName(curr->event);
}
void visitNop(Nop* curr) {}
void visitUnreachable(Unreachable* curr) {}
void visitPop(Pop* curr) {}
void visitTupleMake(TupleMake* curr) {}
void visitTupleExtract(TupleExtract* curr) {
visitor.visitIndex(curr->index);
}
void visitI31New(I31New* curr) {}
void visitI31Get(I31Get* curr) { visitor.visitInt(curr->signed_); }
void visitRefTest(RefTest* curr) {
WASM_UNREACHABLE("TODO (gc): ref.test");
}
void visitRefCast(RefCast* curr) {
WASM_UNREACHABLE("TODO (gc): ref.cast");
}
void visitBrOnCast(BrOnCast* curr) {
WASM_UNREACHABLE("TODO (gc): br_on_cast");
}
void visitRttCanon(RttCanon* curr) {
WASM_UNREACHABLE("TODO (gc): rtt.canon");
}
void visitRttSub(RttSub* curr) { WASM_UNREACHABLE("TODO (gc): rtt.sub"); }
void visitStructNew(StructNew* curr) {
WASM_UNREACHABLE("TODO (gc): struct.new");
}
void visitStructGet(StructGet* curr) {
WASM_UNREACHABLE("TODO (gc): struct.get");
}
void visitStructSet(StructSet* curr) {
WASM_UNREACHABLE("TODO (gc): struct.set");
}
void visitArrayNew(ArrayNew* curr) {
WASM_UNREACHABLE("TODO (gc): array.new");
}
void visitArrayGet(ArrayGet* curr) {
WASM_UNREACHABLE("TODO (gc): array.get");
}
void visitArraySet(ArraySet* curr) {
WASM_UNREACHABLE("TODO (gc): array.set");
}
void visitArrayLen(ArrayLen* curr) {
WASM_UNREACHABLE("TODO (gc): array.len");
}
} singleton(curr, visitor);
}
}
bool ExpressionAnalyzer::flexibleEqual(Expression* left,
Expression* right,
ExprComparer comparer) {
struct Comparer {
std::map<Name, Name> rightNames;
std::vector<Expression*> leftStack;
std::vector<Expression*> rightStack;
struct Immediates {
Comparer& parent;
Immediates(Comparer& parent) : parent(parent) {}
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;
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); }
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 (iter != parent.rightNames.end()) {
leftName = iter->second;
}
if (leftName != rightName) {
return false;
}
}
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;
}
bool operator!=(const Immediates& other) { return !(*this == other); }
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;
}
return true;
}
bool compare(Expression* left, Expression* right, ExprComparer comparer) {
Immediates leftImmediates(*this), rightImmediates(*this);
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; }
if (left->_id != right->_id) {
return false;
}
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 {
visitImmediates(left, leftImmediates);
visitImmediates(right, rightImmediates);
if (leftImmediates != rightImmediates) {
return false;
}
leftImmediates.clear();
rightImmediates.clear();
}
Index counter = 0;
for (auto* child : ChildIterator(left)) {
leftStack.push_back(child);
counter++;
}
for (auto* child : ChildIterator(right)) {
rightStack.push_back(child);
counter--;
}
if (counter != 0) {
return false;
}
}
if (leftStack.size() > 0 || rightStack.size() > 0) {
return false;
}
return true;
}
};
return Comparer().compare(left, right, comparer);
}
size_t ExpressionAnalyzer::hash(Expression* curr) {
struct Hasher {
size_t digest = wasm::hash(0);
Index internalCounter = 0;
std::map<Name, Index> internalNames;
ExpressionStack stack;
void noteScopeName(Name curr) {
if (curr.is()) {
internalNames[curr] = internalCounter++;
}
}
Hasher(Expression* curr) {
stack.push_back(curr);
while (stack.size() > 0) {
curr = stack.back();
stack.pop_back();
if (!curr) {
continue;
}
rehash(digest, curr->_id);
rehash(digest, curr->type.getID());
if (auto* block = curr->dynCast<Block>()) {
noteScopeName(block->name);
} else if (auto* loop = curr->dynCast<Loop>()) {
noteScopeName(loop->name);
} else {
visitImmediates(curr, *this);
}
Index counter = 0;
for (auto* child : ChildIterator(curr)) {
stack.push_back(child);
counter++;
}
rehash(digest, counter);
}
}
void visitScopeName(Name curr) {
static_assert(sizeof(Index) == sizeof(int32_t),
"wasm64 will need changes here");
assert(internalNames.find(curr) != internalNames.end());
rehash(digest, internalNames[curr]);
}
void visitNonScopeName(Name curr) { rehash(digest, uint64_t(curr.str)); }
void visitInt(int32_t curr) { rehash(digest, curr); }
void visitLiteral(Literal curr) { rehash(digest, curr); }
void visitType(Type curr) { rehash(digest, curr.getID()); }
void visitIndex(Index curr) {
static_assert(sizeof(Index) == sizeof(uint32_t),
"wasm64 will need changes here");
rehash(digest, curr);
}
void visitAddress(Address curr) { rehash(digest, curr.addr); }
};
return Hasher(curr).digest;
}
}