#ifndef wasm_stack_h
#define wasm_stack_h
#include "ir/branch-utils.h"
#include "ir/properties.h"
#include "pass.h"
#include "wasm-binary.h"
#include "wasm-traversal.h"
#include "wasm.h"
namespace wasm {
class StackInst {
public:
StackInst(MixedArena&) {}
enum Op {
Basic, BlockBegin, BlockEnd, IfBegin, IfElse, IfEnd, LoopBegin, LoopEnd, TryBegin, Catch, TryEnd } op;
Expression* origin;
Type type;
};
class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
public:
BinaryInstWriter(WasmBinaryWriter& parent,
BufferWithRandomAccess& o,
Function* func,
bool sourceMap,
bool DWARF)
: parent(parent), o(o), func(func), sourceMap(sourceMap), DWARF(DWARF) {}
void visit(Expression* curr) {
if (func && !sourceMap) {
parent.writeDebugLocation(curr, func);
}
OverriddenVisitor<BinaryInstWriter>::visit(curr);
if (func && !sourceMap) {
parent.writeDebugLocationEnd(curr, func);
}
}
void visitBlock(Block* curr);
void visitIf(If* curr);
void visitLoop(Loop* curr);
void visitBreak(Break* curr);
void visitSwitch(Switch* curr);
void visitCall(Call* curr);
void visitCallIndirect(CallIndirect* curr);
void visitLocalGet(LocalGet* curr);
void visitLocalSet(LocalSet* curr);
void visitGlobalGet(GlobalGet* curr);
void visitGlobalSet(GlobalSet* curr);
void visitLoad(Load* curr);
void visitStore(Store* curr);
void visitAtomicRMW(AtomicRMW* curr);
void visitAtomicCmpxchg(AtomicCmpxchg* curr);
void visitAtomicWait(AtomicWait* curr);
void visitAtomicNotify(AtomicNotify* curr);
void visitAtomicFence(AtomicFence* curr);
void visitSIMDExtract(SIMDExtract* curr);
void visitSIMDReplace(SIMDReplace* curr);
void visitSIMDShuffle(SIMDShuffle* curr);
void visitSIMDTernary(SIMDTernary* curr);
void visitSIMDShift(SIMDShift* curr);
void visitSIMDLoad(SIMDLoad* curr);
void visitMemoryInit(MemoryInit* curr);
void visitDataDrop(DataDrop* curr);
void visitMemoryCopy(MemoryCopy* curr);
void visitMemoryFill(MemoryFill* curr);
void visitConst(Const* curr);
void visitUnary(Unary* curr);
void visitBinary(Binary* curr);
void visitSelect(Select* curr);
void visitReturn(Return* curr);
void visitMemorySize(MemorySize* curr);
void visitMemoryGrow(MemoryGrow* curr);
void visitRefNull(RefNull* curr);
void visitRefIsNull(RefIsNull* curr);
void visitRefFunc(RefFunc* curr);
void visitRefEq(RefEq* curr);
void visitTry(Try* curr);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
void visitBrOnExn(BrOnExn* curr);
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitDrop(Drop* curr);
void visitPop(Pop* curr);
void visitTupleMake(TupleMake* curr);
void visitTupleExtract(TupleExtract* curr);
void visitI31New(I31New* curr);
void visitI31Get(I31Get* curr);
void visitRefTest(RefTest* curr);
void visitRefCast(RefCast* curr);
void visitBrOnCast(BrOnCast* curr);
void visitRttCanon(RttCanon* curr);
void visitRttSub(RttSub* curr);
void visitStructNew(StructNew* curr);
void visitStructGet(StructGet* curr);
void visitStructSet(StructSet* curr);
void visitArrayNew(ArrayNew* curr);
void visitArrayGet(ArrayGet* curr);
void visitArraySet(ArraySet* curr);
void visitArrayLen(ArrayLen* curr);
void emitResultType(Type type);
void emitIfElse(If* curr);
void emitCatch(Try* curr);
void emitScopeEnd(Expression* curr);
void emitFunctionEnd();
void emitUnreachable();
void mapLocalsAndEmitHeader();
private:
void emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset);
int32_t getBreakIndex(Name name);
WasmBinaryWriter& parent;
BufferWithRandomAccess& o;
Function* func = nullptr;
bool sourceMap;
bool DWARF;
std::vector<Name> breakStack;
std::map<Type, size_t> numLocalsByType;
std::map<std::pair<Index, Index>, size_t> mappedLocals;
std::map<Type, Index> scratchLocals;
void countScratchLocals();
void setScratchLocals();
};
template<typename SubType>
class BinaryenIRWriter : public Visitor<BinaryenIRWriter<SubType>> {
public:
BinaryenIRWriter(Function* func) : func(func) {}
void write();
void visit(Expression* curr);
void visitBlock(Block* curr);
void visitIf(If* curr);
void visitLoop(Loop* curr);
void visitTry(Try* curr);
protected:
Function* func = nullptr;
private:
void emit(Expression* curr) { static_cast<SubType*>(this)->emit(curr); }
void emitHeader() { static_cast<SubType*>(this)->emitHeader(); }
void emitIfElse(If* curr) { static_cast<SubType*>(this)->emitIfElse(curr); }
void emitCatch(Try* curr) { static_cast<SubType*>(this)->emitCatch(curr); }
void emitScopeEnd(Expression* curr) {
static_cast<SubType*>(this)->emitScopeEnd(curr);
}
void emitFunctionEnd() { static_cast<SubType*>(this)->emitFunctionEnd(); }
void emitUnreachable() { static_cast<SubType*>(this)->emitUnreachable(); }
void emitDebugLocation(Expression* curr) {
static_cast<SubType*>(this)->emitDebugLocation(curr);
}
void visitPossibleBlockContents(Expression* curr);
};
template<typename SubType> void BinaryenIRWriter<SubType>::write() {
assert(func && "BinaryenIRWriter: function is not set");
emitHeader();
visitPossibleBlockContents(func->body);
emitFunctionEnd();
}
template<typename SubType>
void BinaryenIRWriter<SubType>::visitPossibleBlockContents(Expression* curr) {
auto* block = curr->dynCast<Block>();
if (!block || BranchUtils::BranchSeeker::has(block, block->name)) {
visit(curr);
return;
}
for (auto* child : block->list) {
visit(child);
if (child->type == Type::unreachable) {
break;
}
}
}
template<typename SubType>
void BinaryenIRWriter<SubType>::visit(Expression* curr) {
emitDebugLocation(curr);
bool hasUnreachableChild = false;
for (auto* child : ValueChildIterator(curr)) {
visit(child);
if (child->type == Type::unreachable) {
hasUnreachableChild = true;
break;
}
}
if (hasUnreachableChild) {
return;
}
if (Properties::isControlFlowStructure(curr)) {
Visitor<BinaryenIRWriter>::visit(curr);
} else {
emit(curr);
}
}
template<typename SubType>
void BinaryenIRWriter<SubType>::visitBlock(Block* curr) {
auto visitChildren = [this](Block* curr, Index from) {
auto& list = curr->list;
while (from < list.size()) {
auto* child = list[from];
visit(child);
if (child->type == Type::unreachable) {
break;
}
++from;
}
};
auto afterChildren = [this](Block* curr) {
emitScopeEnd(curr);
if (curr->type == Type::unreachable) {
emitUnreachable();
}
};
if (!curr->list.empty() && curr->list[0]->is<Block>()) {
std::vector<Block*> parents;
Block* child;
while (!curr->list.empty() && (child = curr->list[0]->dynCast<Block>())) {
parents.push_back(curr);
emit(curr);
curr = child;
}
emit(curr);
visitChildren(curr, 0);
afterChildren(curr);
bool childUnreachable = curr->type == Type::unreachable;
while (!parents.empty()) {
auto* parent = parents.back();
parents.pop_back();
if (!childUnreachable) {
visitChildren(parent, 1);
}
afterChildren(parent);
childUnreachable = parent->type == Type::unreachable;
}
return;
}
emit(curr);
visitChildren(curr, 0);
afterChildren(curr);
}
template<typename SubType> void BinaryenIRWriter<SubType>::visitIf(If* curr) {
emit(curr);
visitPossibleBlockContents(curr->ifTrue);
if (curr->ifFalse) {
emitIfElse(curr);
visitPossibleBlockContents(curr->ifFalse);
}
emitScopeEnd(curr);
if (curr->type == Type::unreachable) {
assert(curr->ifFalse);
emitUnreachable();
}
}
template<typename SubType>
void BinaryenIRWriter<SubType>::visitLoop(Loop* curr) {
emit(curr);
visitPossibleBlockContents(curr->body);
emitScopeEnd(curr);
if (curr->type == Type::unreachable) {
emitUnreachable();
}
}
template<typename SubType> void BinaryenIRWriter<SubType>::visitTry(Try* curr) {
emit(curr);
visitPossibleBlockContents(curr->body);
emitCatch(curr);
visitPossibleBlockContents(curr->catchBody);
emitScopeEnd(curr);
if (curr->type == Type::unreachable) {
emitUnreachable();
}
}
class BinaryenIRToBinaryWriter
: public BinaryenIRWriter<BinaryenIRToBinaryWriter> {
public:
BinaryenIRToBinaryWriter(WasmBinaryWriter& parent,
BufferWithRandomAccess& o,
Function* func = nullptr,
bool sourceMap = false,
bool DWARF = false)
: BinaryenIRWriter<BinaryenIRToBinaryWriter>(func), parent(parent),
writer(parent, o, func, sourceMap, DWARF), sourceMap(sourceMap) {}
void visit(Expression* curr) {
BinaryenIRWriter<BinaryenIRToBinaryWriter>::visit(curr);
}
void emit(Expression* curr) { writer.visit(curr); }
void emitHeader() {
if (func->prologLocation.size()) {
parent.writeDebugLocation(*func->prologLocation.begin());
}
writer.mapLocalsAndEmitHeader();
}
void emitIfElse(If* curr) { writer.emitIfElse(curr); }
void emitCatch(Try* curr) { writer.emitCatch(curr); }
void emitScopeEnd(Expression* curr) { writer.emitScopeEnd(curr); }
void emitFunctionEnd() {
if (func->epilogLocation.size()) {
parent.writeDebugLocation(*func->epilogLocation.begin());
}
writer.emitFunctionEnd();
}
void emitUnreachable() { writer.emitUnreachable(); }
void emitDebugLocation(Expression* curr) {
if (sourceMap) {
parent.writeDebugLocation(curr, func);
}
}
private:
WasmBinaryWriter& parent;
BinaryInstWriter writer;
bool sourceMap;
};
class StackIRGenerator : public BinaryenIRWriter<StackIRGenerator> {
public:
StackIRGenerator(Module& module, Function* func)
: BinaryenIRWriter<StackIRGenerator>(func), module(module) {}
void emit(Expression* curr);
void emitScopeEnd(Expression* curr);
void emitHeader() {}
void emitIfElse(If* curr) {
stackIR.push_back(makeStackInst(StackInst::IfElse, curr));
}
void emitCatch(Try* curr) {
stackIR.push_back(makeStackInst(StackInst::Catch, curr));
}
void emitFunctionEnd() {}
void emitUnreachable() {
stackIR.push_back(makeStackInst(Builder(module).makeUnreachable()));
}
void emitDebugLocation(Expression* curr) {}
StackIR& getStackIR() { return stackIR; }
private:
StackInst* makeStackInst(StackInst::Op op, Expression* origin);
StackInst* makeStackInst(Expression* origin) {
return makeStackInst(StackInst::Basic, origin);
}
Module& module;
StackIR stackIR; };
class StackIRToBinaryWriter {
public:
StackIRToBinaryWriter(WasmBinaryWriter& parent,
BufferWithRandomAccess& o,
Function* func)
: writer(parent, o, func, false , false ),
func(func) {}
void write();
private:
BinaryInstWriter writer;
Function* func;
};
}
#endif