blob: 6e1150981f43d9eb58cae459e4b602fd1366cbbb [file] [log] [blame] [edit]
/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_stack_h
#define wasm_stack_h
#include "wasm.h"
#include "wasm-binary.h"
#include "wasm-traversal.h"
#include "ir/branch-utils.h"
#include "pass.h"
namespace wasm {
// Stack IR: an IR that represents code at the wasm binary format level,
// that is, a stack machine. Binaryen IR is *almost* identical to this,
// but as documented in README.md, there are a few differences, intended
// to make Binaryen IR fast and flexible for maximal optimization. Stack
// IR, on the other hand, is designed to optimize a few final things that
// can only really be done when modeling the stack machine format precisely.
// Currently the benefits of Stack IR are minor, less than 1% reduction in
// code size. For that reason it is just a secondary IR, run optionally
// after the main IR has been optimized. However, if we improve Stack IR
// optimizations to a point where they have a significant impact, it's
// possible that could motivate investigating replacing the main IR with Stack
// IR (so that we have just a single IR).
// A StackIR instance (see wasm.h) contains a linear sequence of
// stack instructions. This representation is very simple: just a single vector of
// all instructions, in order.
// * nullptr is allowed in the vector, representing something to skip.
// This is useful as a common thing optimizations do is remove instructions,
// so this way we can do so without compacting the vector all the time.
// A Stack IR instruction. Most just directly reflect a Binaryen IR node,
// but we need extra ones for certain things.
class StackInst {
public:
StackInst(MixedArena&) {}
enum Op {
Basic, // an instruction directly corresponding to a non-control-flow
// Binaryen IR node
BlockBegin, // the beginning of a block
BlockEnd, // the ending of a block
IfBegin, // the beginning of a if
IfElse, // the else of a if
IfEnd, // the ending of a if
LoopBegin, // the beginning of a loop
LoopEnd, // the ending of a loop
} op;
Expression* origin; // the expression this originates from
Type type; // the type - usually identical to the origin type, but
// e.g. wasm has no unreachable blocks, they must be none
};
//
// StackWriter: Writes out binary format stack machine code for a Binaryen IR expression
//
// A stack writer has one of three modes:
// * Binaryen2Binary: directly writes the expression to wasm binary
// * Binaryen2Stack: queues the expressions linearly, in Stack IR (SIR)
// * Stack2Binary: emits SIR to wasm binary
//
// Direct writing, in Binaryen2Binary, is fast. Otherwise, Binaryen2Stack
// lets you optimize the Stack IR before running Stack2Binary (but the cost
// is that the extra IR in the middle makes things 20% slower than direct
// Binaryen2Binary).
//
// To reduce the amount of boilerplate code here, we implement all 3 in
// a single class, templated on the mode. This allows compilers to trivially
// optimize out irrelevant code paths, and there should be no runtime
// downside.
//
enum class StackWriterMode {
Binaryen2Binary, Binaryen2Stack, Stack2Binary
};
template<StackWriterMode Mode, typename Parent>
class StackWriter : public Visitor<StackWriter<Mode, Parent>> {
public:
StackWriter(Parent& parent, BufferWithRandomAccess& o, bool sourceMap=false, bool debug=false)
: parent(parent), o(o), sourceMap(sourceMap), debug(debug), allocator(parent.getModule()->allocator) {}
StackIR stackIR; // filled in Binaryen2Stack, read in Stack2Binary
std::map<Type, size_t> numLocalsByType; // type => number of locals of that type in the compact form
// visits a node, emitting the proper code for it
void visit(Expression* curr);
// emits a node, but if it is a block with no name, emit a list of its contents
void visitPossibleBlockContents(Expression* curr);
// visits a child node. (in some modes we may not want to visit children,
// that logic is handled here)
void visitChild(Expression* curr);
void visitBlock(Block* curr);
void visitBlockEnd(Block* curr);
void visitIf(If* curr);
void visitIfElse(If* curr);
void visitIfEnd(If* curr);
void visitLoop(Loop* curr);
void visitLoopEnd(Loop* curr);
void visitBreak(Break* curr);
void visitSwitch(Switch* curr);
void visitCall(Call* curr);
void visitCallIndirect(CallIndirect* curr);
void visitGetLocal(GetLocal* curr);
void visitSetLocal(SetLocal* curr);
void visitGetGlobal(GetGlobal* curr);
void visitSetGlobal(SetGlobal* curr);
void visitLoad(Load* curr);
void visitStore(Store* curr);
void visitAtomicRMW(AtomicRMW* curr);
void visitAtomicCmpxchg(AtomicCmpxchg* curr);
void visitAtomicWait(AtomicWait* curr);
void visitAtomicWake(AtomicWake* curr);
void visitConst(Const* curr);
void visitUnary(Unary* curr);
void visitBinary(Binary* curr);
void visitSelect(Select* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitDrop(Drop* curr);
// We need to emit extra unreachable opcodes in some cases
void emitExtraUnreachable();
// If we are in Binaryen2Stack, then this adds the item to the
// stack IR and returns true, which is all we need to do for
// non-control flow expressions.
bool justAddToStack(Expression* curr);
void setFunction(Function* funcInit) {
func = funcInit;
}
void mapLocalsAndEmitHeader();
protected:
Parent& parent;
BufferWithRandomAccess& o;
bool sourceMap;
bool debug;
MixedArena& allocator;
Function* func;
std::map<Index, size_t> mappedLocals; // local index => index in compact form of [all int32s][all int64s]etc
std::vector<Name> breakStack;
int32_t getBreakIndex(Name name);
void emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset);
void finishFunctionBody();
StackInst* makeStackInst(StackInst::Op op, Expression* origin);
StackInst* makeStackInst(Expression* origin) {
return makeStackInst(StackInst::Basic, origin);
}
};
// Write out a single expression, such as an offset for a global segment.
template<typename Parent>
class ExpressionStackWriter : StackWriter<StackWriterMode::Binaryen2Binary, Parent> {
public:
ExpressionStackWriter(Expression* curr, Parent& parent, BufferWithRandomAccess& o, bool debug=false) :
StackWriter<StackWriterMode::Binaryen2Binary, Parent>(parent, o, /* sourceMap= */ false, debug) {
this->visit(curr);
}
};
// Write out a function body, including the local header info.
template<typename Parent>
class FunctionStackWriter : StackWriter<StackWriterMode::Binaryen2Binary, Parent> {
public:
FunctionStackWriter(Function* funcInit, Parent& parent, BufferWithRandomAccess& o, bool sourceMap=false, bool debug=false) :
StackWriter<StackWriterMode::Binaryen2Binary, Parent>(parent, o, sourceMap, debug) {
this->setFunction(funcInit);
this->mapLocalsAndEmitHeader();
this->visitPossibleBlockContents(this->func->body);
this->finishFunctionBody();
}
};
// Use Stack IR to write the function body
template<typename Parent>
class StackIRFunctionStackWriter : StackWriter<StackWriterMode::Stack2Binary, Parent> {
public:
StackIRFunctionStackWriter(Function* funcInit, Parent& parent, BufferWithRandomAccess& o, bool debug=false) :
StackWriter<StackWriterMode::Stack2Binary, Parent>(parent, o, false, debug) {
this->setFunction(funcInit);
this->mapLocalsAndEmitHeader();
for (auto* inst : *funcInit->stackIR) {
if (!inst) continue; // a nullptr is just something we can skip
switch (inst->op) {
case StackInst::Basic:
case StackInst::BlockBegin:
case StackInst::IfBegin:
case StackInst::LoopBegin: {
this->visit(inst->origin);
break;
}
case StackInst::BlockEnd: {
this->visitBlockEnd(inst->origin->template cast<Block>());
break;
}
case StackInst::IfElse: {
this->visitIfElse(inst->origin->template cast<If>());
break;
}
case StackInst::IfEnd: {
this->visitIfEnd(inst->origin->template cast<If>());
break;
}
case StackInst::LoopEnd: {
this->visitLoopEnd(inst->origin->template cast<Loop>());
break;
}
default: WASM_UNREACHABLE();
}
}
this->finishFunctionBody();
}
};
//
// Implementations
//
// StackWriter
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::mapLocalsAndEmitHeader() {
if (func->prologLocation.size()) {
parent.writeDebugLocation(*func->prologLocation.begin());
}
// Map them
for (Index i = 0; i < func->getNumParams(); i++) {
size_t curr = mappedLocals.size();
mappedLocals[i] = curr;
}
for (auto type : func->vars) {
numLocalsByType[type]++;
}
std::map<Type, size_t> currLocalsByType;
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
size_t index = func->getVarIndexBase();
Type type = func->getLocalType(i);
currLocalsByType[type]++; // increment now for simplicity, must decrement it in returns
if (type == i32) {
mappedLocals[i] = index + currLocalsByType[i32] - 1;
continue;
}
index += numLocalsByType[i32];
if (type == i64) {
mappedLocals[i] = index + currLocalsByType[i64] - 1;
continue;
}
index += numLocalsByType[i64];
if (type == f32) {
mappedLocals[i] = index + currLocalsByType[f32] - 1;
continue;
}
index += numLocalsByType[f32];
if (type == f64) {
mappedLocals[i] = index + currLocalsByType[f64] - 1;
continue;
}
index += numLocalsByType[f64];
if (type == v128) {
mappedLocals[i] = index + currLocalsByType[v128] - 1;
continue;
}
WASM_UNREACHABLE();
}
// Emit them.
o << U32LEB(
(numLocalsByType[i32] ? 1 : 0) +
(numLocalsByType[i64] ? 1 : 0) +
(numLocalsByType[f32] ? 1 : 0) +
(numLocalsByType[f64] ? 1 : 0) +
(numLocalsByType[v128] ? 1 : 0)
);
if (numLocalsByType[i32]) o << U32LEB(numLocalsByType[i32]) << binaryType(i32);
if (numLocalsByType[i64]) o << U32LEB(numLocalsByType[i64]) << binaryType(i64);
if (numLocalsByType[f32]) o << U32LEB(numLocalsByType[f32]) << binaryType(f32);
if (numLocalsByType[f64]) o << U32LEB(numLocalsByType[f64]) << binaryType(f64);
if (numLocalsByType[v128]) o << U32LEB(numLocalsByType[v128]) << binaryType(v128);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visit(Expression* curr) {
if (Mode == StackWriterMode::Binaryen2Binary && sourceMap) {
parent.writeDebugLocation(curr, func);
}
Visitor<StackWriter>::visit(curr);
}
// emits a node, but if it is a block with no name, emit a list of its contents
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitPossibleBlockContents(Expression* curr) {
auto* block = curr->dynCast<Block>();
if (!block || BranchUtils::BranchSeeker::hasNamed(block, block->name)) {
visitChild(curr);
return;
}
for (auto* child : block->list) {
visitChild(child);
}
if (block->type == unreachable && block->list.back()->type != unreachable) {
// similar to in visitBlock, here we could skip emitting the block itself,
// but must still end the 'block' (the contents, really) with an unreachable
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitChild(Expression* curr) {
// In stack => binary, we don't need to visit child nodes, everything
// is already in the linear stream.
if (Mode != StackWriterMode::Stack2Binary) {
visit(curr);
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitBlock(Block* curr) {
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::BlockBegin, curr));
} else {
if (debug) std::cerr << "zz node: Block" << std::endl;
o << int8_t(BinaryConsts::Block);
o << binaryType(curr->type != unreachable ? curr->type : none);
}
breakStack.push_back(curr->name); // TODO: we don't need to do this in Binaryen2Stack
Index i = 0;
for (auto* child : curr->list) {
if (debug) std::cerr << " " << size_t(curr) << "\n zz Block element " << i++ << std::endl;
visitChild(child);
}
// in Stack2Binary the block ending is in the stream later on
if (Mode == StackWriterMode::Stack2Binary) {
return;
}
visitBlockEnd(curr);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitBlockEnd(Block* curr) {
if (curr->type == unreachable) {
// an unreachable block is one that cannot be exited. We cannot encode this directly
// in wasm, where blocks must be none,i32,i64,f32,f64. Since the block cannot be
// exited, we can emit an unreachable at the end, and that will always be valid,
// and then the block is ok as a none
emitExtraUnreachable();
}
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::BlockEnd, curr));
} else {
o << int8_t(BinaryConsts::End);
}
assert(!breakStack.empty());
breakStack.pop_back();
if (curr->type == unreachable) {
// and emit an unreachable *outside* the block too, so later things can pop anything
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitIf(If* curr) {
if (debug) std::cerr << "zz node: If" << std::endl;
if (curr->condition->type == unreachable) {
// this if-else is unreachable because of the condition, i.e., the condition
// does not exit. So don't emit the if, but do consume the condition
visitChild(curr->condition);
emitExtraUnreachable();
return;
}
visitChild(curr->condition);
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::IfBegin, curr));
} else {
o << int8_t(BinaryConsts::If);
o << binaryType(curr->type != unreachable ? curr->type : none);
}
breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one
// TODO: optimize this in Stack IR (if child is a block, we
// may break to this instead)
visitPossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible
if (Mode == StackWriterMode::Stack2Binary) {
return;
}
if (curr->ifFalse) {
visitIfElse(curr);
}
visitIfEnd(curr);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitIfElse(If* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::IfElse, curr));
} else {
o << int8_t(BinaryConsts::Else);
}
breakStack.push_back(IMPOSSIBLE_CONTINUE); // TODO ditto
visitPossibleBlockContents(curr->ifFalse);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitIfEnd(If* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::IfEnd, curr));
} else {
o << int8_t(BinaryConsts::End);
}
if (curr->type == unreachable) {
// we already handled the case of the condition being unreachable. otherwise,
// we may still be unreachable, if we are an if-else with both sides unreachable.
// wasm does not allow this to be emitted directly, so we must do something more. we could do
// better, but for now we emit an extra unreachable instruction after the if, so it is not consumed itself,
assert(curr->ifFalse);
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitLoop(Loop* curr) {
if (debug) std::cerr << "zz node: Loop" << std::endl;
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::LoopBegin, curr));
} else {
o << int8_t(BinaryConsts::Loop);
o << binaryType(curr->type != unreachable ? curr->type : none);
}
breakStack.push_back(curr->name);
visitPossibleBlockContents(curr->body);
if (Mode == StackWriterMode::Stack2Binary) {
return;
}
visitLoopEnd(curr);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitLoopEnd(Loop* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
if (curr->type == unreachable) {
// we emitted a loop without a return type, and the body might be
// block contents, so ensure it is not consumed
emitExtraUnreachable();
}
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(StackInst::LoopEnd, curr));
} else {
o << int8_t(BinaryConsts::End);
}
if (curr->type == unreachable) {
// we emitted a loop without a return type, so it must not be consumed
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitBreak(Break* curr) {
if (debug) std::cerr << "zz node: Break" << std::endl;
if (curr->value) {
visitChild(curr->value);
}
if (curr->condition) visitChild(curr->condition);
if (!justAddToStack(curr)) {
o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br)
<< U32LEB(getBreakIndex(curr->name));
}
if (curr->condition && curr->type == unreachable) {
// a br_if is normally none or emits a value. if it is unreachable,
// then either the condition or the value is unreachable, which is
// extremely rare, and may require us to make the stack polymorphic
// (if the block we branch to has a value, we may lack one as we
// are not a reachable branch; the wasm spec on the other hand does
// presume the br_if emits a value of the right type, even if it
// popped unreachable)
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitSwitch(Switch* curr) {
if (debug) std::cerr << "zz node: Switch" << std::endl;
if (curr->value) {
visitChild(curr->value);
}
visitChild(curr->condition);
if (!BranchUtils::isBranchReachable(curr)) {
// if the branch is not reachable, then it's dangerous to emit it, as
// wasm type checking rules are different, especially in unreachable
// code. so just don't emit that unreachable code.
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->targets.size());
for (auto target : curr->targets) {
o << U32LEB(getBreakIndex(target));
}
o << U32LEB(getBreakIndex(curr->default_));
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitCall(Call* curr) {
if (debug) std::cerr << "zz node: Call" << std::endl;
for (auto* operand : curr->operands) {
visitChild(operand);
}
if (!justAddToStack(curr)) {
o << int8_t(BinaryConsts::CallFunction) << U32LEB(parent.getFunctionIndex(curr->target));
}
if (curr->type == unreachable) { // TODO FIXME: this and similar can be removed
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitCallIndirect(CallIndirect* curr) {
if (debug) std::cerr << "zz node: CallIndirect" << std::endl;
for (auto* operand : curr->operands) {
visitChild(operand);
}
visitChild(curr->target);
if (!justAddToStack(curr)) {
o << int8_t(BinaryConsts::CallIndirect)
<< U32LEB(parent.getFunctionTypeIndex(curr->fullType))
<< U32LEB(0); // Reserved flags field
}
if (curr->type == unreachable) {
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitGetLocal(GetLocal* curr) {
if (debug) std::cerr << "zz node: GetLocal " << (o.size() + 1) << std::endl;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::GetLocal) << U32LEB(mappedLocals[curr->index]);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitSetLocal(SetLocal* curr) {
if (debug) std::cerr << "zz node: Set|TeeLocal" << std::endl;
visitChild(curr->value);
if (!justAddToStack(curr)) {
o << int8_t(curr->isTee() ? BinaryConsts::TeeLocal : BinaryConsts::SetLocal) << U32LEB(mappedLocals[curr->index]);
}
if (curr->type == unreachable) {
emitExtraUnreachable();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitGetGlobal(GetGlobal* curr) {
if (debug) std::cerr << "zz node: GetGlobal " << (o.size() + 1) << std::endl;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::GetGlobal) << U32LEB(parent.getGlobalIndex(curr->name));
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitSetGlobal(SetGlobal* curr) {
if (debug) std::cerr << "zz node: SetGlobal" << std::endl;
visitChild(curr->value);
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::SetGlobal) << U32LEB(parent.getGlobalIndex(curr->name));
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitLoad(Load* curr) {
if (debug) std::cerr << "zz node: Load" << std::endl;
visitChild(curr->ptr);
if (curr->type == unreachable) {
// don't even emit it; we don't know the right type
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
if (!curr->isAtomic) {
switch (curr->type) {
case i32: {
switch (curr->bytes) {
case 1: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S : BinaryConsts::I32LoadMem8U); break;
case 2: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S : BinaryConsts::I32LoadMem16U); break;
case 4: o << int8_t(BinaryConsts::I32LoadMem); break;
default: abort();
}
break;
}
case i64: {
switch (curr->bytes) {
case 1: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S : BinaryConsts::I64LoadMem8U); break;
case 2: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S : BinaryConsts::I64LoadMem16U); break;
case 4: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S : BinaryConsts::I64LoadMem32U); break;
case 8: o << int8_t(BinaryConsts::I64LoadMem); break;
default: abort();
}
break;
}
case f32: o << int8_t(BinaryConsts::F32LoadMem); break;
case f64: o << int8_t(BinaryConsts::F64LoadMem); break;
case v128: assert(false && "v128 not implemented yet");
case unreachable: return; // the pointer is unreachable, so we are never reached; just don't emit a load
case none: WASM_UNREACHABLE();
}
} else {
o << int8_t(BinaryConsts::AtomicPrefix);
switch (curr->type) {
case i32: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I32AtomicLoad8U); break;
case 2: o << int8_t(BinaryConsts::I32AtomicLoad16U); break;
case 4: o << int8_t(BinaryConsts::I32AtomicLoad); break;
default: WASM_UNREACHABLE();
}
break;
}
case i64: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I64AtomicLoad8U); break;
case 2: o << int8_t(BinaryConsts::I64AtomicLoad16U); break;
case 4: o << int8_t(BinaryConsts::I64AtomicLoad32U); break;
case 8: o << int8_t(BinaryConsts::I64AtomicLoad); break;
default: WASM_UNREACHABLE();
}
break;
}
case unreachable: return;
default: WASM_UNREACHABLE();
}
}
emitMemoryAccess(curr->align, curr->bytes, curr->offset);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitStore(Store* curr) {
if (debug) std::cerr << "zz node: Store" << std::endl;
visitChild(curr->ptr);
visitChild(curr->value);
if (curr->type == unreachable) {
// don't even emit it; we don't know the right type
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
if (!curr->isAtomic) {
switch (curr->valueType) {
case i32: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I32StoreMem8); break;
case 2: o << int8_t(BinaryConsts::I32StoreMem16); break;
case 4: o << int8_t(BinaryConsts::I32StoreMem); break;
default: abort();
}
break;
}
case i64: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I64StoreMem8); break;
case 2: o << int8_t(BinaryConsts::I64StoreMem16); break;
case 4: o << int8_t(BinaryConsts::I64StoreMem32); break;
case 8: o << int8_t(BinaryConsts::I64StoreMem); break;
default: abort();
}
break;
}
case f32: o << int8_t(BinaryConsts::F32StoreMem); break;
case f64: o << int8_t(BinaryConsts::F64StoreMem); break;
case v128: assert(false && "v128 not implemented yet");
case none:
case unreachable: WASM_UNREACHABLE();
}
} else {
o << int8_t(BinaryConsts::AtomicPrefix);
switch (curr->valueType) {
case i32: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I32AtomicStore8); break;
case 2: o << int8_t(BinaryConsts::I32AtomicStore16); break;
case 4: o << int8_t(BinaryConsts::I32AtomicStore); break;
default: WASM_UNREACHABLE();
}
break;
}
case i64: {
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I64AtomicStore8); break;
case 2: o << int8_t(BinaryConsts::I64AtomicStore16); break;
case 4: o << int8_t(BinaryConsts::I64AtomicStore32); break;
case 8: o << int8_t(BinaryConsts::I64AtomicStore); break;
default: WASM_UNREACHABLE();
}
break;
}
default: WASM_UNREACHABLE();
}
}
emitMemoryAccess(curr->align, curr->bytes, curr->offset);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitAtomicRMW(AtomicRMW* curr) {
if (debug) std::cerr << "zz node: AtomicRMW" << std::endl;
visitChild(curr->ptr);
// stop if the rest isn't reachable anyhow
if (curr->ptr->type == unreachable) return;
visitChild(curr->value);
if (curr->value->type == unreachable) return;
if (curr->type == unreachable) {
// don't even emit it; we don't know the right type
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::AtomicPrefix);
#define CASE_FOR_OP(Op) \
case Op: \
switch (curr->type) { \
case i32: \
switch (curr->bytes) { \
case 1: o << int8_t(BinaryConsts::I32AtomicRMW##Op##8U); break; \
case 2: o << int8_t(BinaryConsts::I32AtomicRMW##Op##16U); break; \
case 4: o << int8_t(BinaryConsts::I32AtomicRMW##Op); break; \
default: WASM_UNREACHABLE(); \
} \
break; \
case i64: \
switch (curr->bytes) { \
case 1: o << int8_t(BinaryConsts::I64AtomicRMW##Op##8U); break; \
case 2: o << int8_t(BinaryConsts::I64AtomicRMW##Op##16U); break; \
case 4: o << int8_t(BinaryConsts::I64AtomicRMW##Op##32U); break; \
case 8: o << int8_t(BinaryConsts::I64AtomicRMW##Op); break; \
default: WASM_UNREACHABLE(); \
} \
break; \
default: WASM_UNREACHABLE(); \
} \
break
switch(curr->op) {
CASE_FOR_OP(Add);
CASE_FOR_OP(Sub);
CASE_FOR_OP(And);
CASE_FOR_OP(Or);
CASE_FOR_OP(Xor);
CASE_FOR_OP(Xchg);
default: WASM_UNREACHABLE();
}
#undef CASE_FOR_OP
emitMemoryAccess(curr->bytes, curr->bytes, curr->offset);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
if (debug) std::cerr << "zz node: AtomicCmpxchg" << std::endl;
visitChild(curr->ptr);
// stop if the rest isn't reachable anyhow
if (curr->ptr->type == unreachable) return;
visitChild(curr->expected);
if (curr->expected->type == unreachable) return;
visitChild(curr->replacement);
if (curr->replacement->type == unreachable) return;
if (curr->type == unreachable) {
// don't even emit it; we don't know the right type
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::AtomicPrefix);
switch (curr->type) {
case i32:
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I32AtomicCmpxchg8U); break;
case 2: o << int8_t(BinaryConsts::I32AtomicCmpxchg16U); break;
case 4: o << int8_t(BinaryConsts::I32AtomicCmpxchg); break;
default: WASM_UNREACHABLE();
}
break;
case i64:
switch (curr->bytes) {
case 1: o << int8_t(BinaryConsts::I64AtomicCmpxchg8U); break;
case 2: o << int8_t(BinaryConsts::I64AtomicCmpxchg16U); break;
case 4: o << int8_t(BinaryConsts::I64AtomicCmpxchg32U); break;
case 8: o << int8_t(BinaryConsts::I64AtomicCmpxchg); break;
default: WASM_UNREACHABLE();
}
break;
default: WASM_UNREACHABLE();
}
emitMemoryAccess(curr->bytes, curr->bytes, curr->offset);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitAtomicWait(AtomicWait* curr) {
if (debug) std::cerr << "zz node: AtomicWait" << std::endl;
visitChild(curr->ptr);
// stop if the rest isn't reachable anyhow
if (curr->ptr->type == unreachable) return;
visitChild(curr->expected);
if (curr->expected->type == unreachable) return;
visitChild(curr->timeout);
if (curr->timeout->type == unreachable) return;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::AtomicPrefix);
switch (curr->expectedType) {
case i32: {
o << int8_t(BinaryConsts::I32AtomicWait);
emitMemoryAccess(4, 4, 0);
break;
}
case i64: {
o << int8_t(BinaryConsts::I64AtomicWait);
emitMemoryAccess(8, 8, 0);
break;
}
default: WASM_UNREACHABLE();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitAtomicWake(AtomicWake* curr) {
if (debug) std::cerr << "zz node: AtomicWake" << std::endl;
visitChild(curr->ptr);
// stop if the rest isn't reachable anyhow
if (curr->ptr->type == unreachable) return;
visitChild(curr->wakeCount);
if (curr->wakeCount->type == unreachable) return;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicWake);
emitMemoryAccess(4, 4, 0);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitConst(Const* curr) {
if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl;
if (justAddToStack(curr)) return;
switch (curr->type) {
case i32: {
o << int8_t(BinaryConsts::I32Const) << S32LEB(curr->value.geti32());
break;
}
case i64: {
o << int8_t(BinaryConsts::I64Const) << S64LEB(curr->value.geti64());
break;
}
case f32: {
o << int8_t(BinaryConsts::F32Const) << curr->value.reinterpreti32();
break;
}
case f64: {
o << int8_t(BinaryConsts::F64Const) << curr->value.reinterpreti64();
break;
}
case v128: assert(false && "v128 not implemented yet");
case none:
case unreachable: WASM_UNREACHABLE();
}
if (debug) std::cerr << "zz const node done.\n";
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitUnary(Unary* curr) {
if (debug) std::cerr << "zz node: Unary" << std::endl;
visitChild(curr->value);
if (curr->type == unreachable) {
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
switch (curr->op) {
case ClzInt32: o << int8_t(BinaryConsts::I32Clz); break;
case CtzInt32: o << int8_t(BinaryConsts::I32Ctz); break;
case PopcntInt32: o << int8_t(BinaryConsts::I32Popcnt); break;
case EqZInt32: o << int8_t(BinaryConsts::I32EqZ); break;
case ClzInt64: o << int8_t(BinaryConsts::I64Clz); break;
case CtzInt64: o << int8_t(BinaryConsts::I64Ctz); break;
case PopcntInt64: o << int8_t(BinaryConsts::I64Popcnt); break;
case EqZInt64: o << int8_t(BinaryConsts::I64EqZ); break;
case NegFloat32: o << int8_t(BinaryConsts::F32Neg); break;
case AbsFloat32: o << int8_t(BinaryConsts::F32Abs); break;
case CeilFloat32: o << int8_t(BinaryConsts::F32Ceil); break;
case FloorFloat32: o << int8_t(BinaryConsts::F32Floor); break;
case TruncFloat32: o << int8_t(BinaryConsts::F32Trunc); break;
case NearestFloat32: o << int8_t(BinaryConsts::F32NearestInt); break;
case SqrtFloat32: o << int8_t(BinaryConsts::F32Sqrt); break;
case NegFloat64: o << int8_t(BinaryConsts::F64Neg); break;
case AbsFloat64: o << int8_t(BinaryConsts::F64Abs); break;
case CeilFloat64: o << int8_t(BinaryConsts::F64Ceil); break;
case FloorFloat64: o << int8_t(BinaryConsts::F64Floor); break;
case TruncFloat64: o << int8_t(BinaryConsts::F64Trunc); break;
case NearestFloat64: o << int8_t(BinaryConsts::F64NearestInt); break;
case SqrtFloat64: o << int8_t(BinaryConsts::F64Sqrt); break;
case ExtendSInt32: o << int8_t(BinaryConsts::I64STruncI32); break;
case ExtendUInt32: o << int8_t(BinaryConsts::I64UTruncI32); break;
case WrapInt64: o << int8_t(BinaryConsts::I32ConvertI64); break;
case TruncUFloat32ToInt32: o << int8_t(BinaryConsts::I32UTruncF32); break;
case TruncUFloat32ToInt64: o << int8_t(BinaryConsts::I64UTruncF32); break;
case TruncSFloat32ToInt32: o << int8_t(BinaryConsts::I32STruncF32); break;
case TruncSFloat32ToInt64: o << int8_t(BinaryConsts::I64STruncF32); break;
case TruncUFloat64ToInt32: o << int8_t(BinaryConsts::I32UTruncF64); break;
case TruncUFloat64ToInt64: o << int8_t(BinaryConsts::I64UTruncF64); break;
case TruncSFloat64ToInt32: o << int8_t(BinaryConsts::I32STruncF64); break;
case TruncSFloat64ToInt64: o << int8_t(BinaryConsts::I64STruncF64); break;
case ConvertUInt32ToFloat32: o << int8_t(BinaryConsts::F32UConvertI32); break;
case ConvertUInt32ToFloat64: o << int8_t(BinaryConsts::F64UConvertI32); break;
case ConvertSInt32ToFloat32: o << int8_t(BinaryConsts::F32SConvertI32); break;
case ConvertSInt32ToFloat64: o << int8_t(BinaryConsts::F64SConvertI32); break;
case ConvertUInt64ToFloat32: o << int8_t(BinaryConsts::F32UConvertI64); break;
case ConvertUInt64ToFloat64: o << int8_t(BinaryConsts::F64UConvertI64); break;
case ConvertSInt64ToFloat32: o << int8_t(BinaryConsts::F32SConvertI64); break;
case ConvertSInt64ToFloat64: o << int8_t(BinaryConsts::F64SConvertI64); break;
case DemoteFloat64: o << int8_t(BinaryConsts::F32ConvertF64); break;
case PromoteFloat32: o << int8_t(BinaryConsts::F64ConvertF32); break;
case ReinterpretFloat32: o << int8_t(BinaryConsts::I32ReinterpretF32); break;
case ReinterpretFloat64: o << int8_t(BinaryConsts::I64ReinterpretF64); break;
case ReinterpretInt32: o << int8_t(BinaryConsts::F32ReinterpretI32); break;
case ReinterpretInt64: o << int8_t(BinaryConsts::F64ReinterpretI64); break;
case ExtendS8Int32: o << int8_t(BinaryConsts::I32ExtendS8); break;
case ExtendS16Int32: o << int8_t(BinaryConsts::I32ExtendS16); break;
case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break;
case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break;
case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break;
case TruncSatSFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF32); break;
case TruncSatUFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF32); break;
case TruncSatSFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF64); break;
case TruncSatUFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF64); break;
case TruncSatSFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF32); break;
case TruncSatUFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF32); break;
case TruncSatSFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF64); break;
case TruncSatUFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF64); break;
case InvalidUnary: WASM_UNREACHABLE();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitBinary(Binary* curr) {
if (debug) std::cerr << "zz node: Binary" << std::endl;
visitChild(curr->left);
visitChild(curr->right);
if (curr->type == unreachable) {
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
switch (curr->op) {
case AddInt32: o << int8_t(BinaryConsts::I32Add); break;
case SubInt32: o << int8_t(BinaryConsts::I32Sub); break;
case MulInt32: o << int8_t(BinaryConsts::I32Mul); break;
case DivSInt32: o << int8_t(BinaryConsts::I32DivS); break;
case DivUInt32: o << int8_t(BinaryConsts::I32DivU); break;
case RemSInt32: o << int8_t(BinaryConsts::I32RemS); break;
case RemUInt32: o << int8_t(BinaryConsts::I32RemU); break;
case AndInt32: o << int8_t(BinaryConsts::I32And); break;
case OrInt32: o << int8_t(BinaryConsts::I32Or); break;
case XorInt32: o << int8_t(BinaryConsts::I32Xor); break;
case ShlInt32: o << int8_t(BinaryConsts::I32Shl); break;
case ShrUInt32: o << int8_t(BinaryConsts::I32ShrU); break;
case ShrSInt32: o << int8_t(BinaryConsts::I32ShrS); break;
case RotLInt32: o << int8_t(BinaryConsts::I32RotL); break;
case RotRInt32: o << int8_t(BinaryConsts::I32RotR); break;
case EqInt32: o << int8_t(BinaryConsts::I32Eq); break;
case NeInt32: o << int8_t(BinaryConsts::I32Ne); break;
case LtSInt32: o << int8_t(BinaryConsts::I32LtS); break;
case LtUInt32: o << int8_t(BinaryConsts::I32LtU); break;
case LeSInt32: o << int8_t(BinaryConsts::I32LeS); break;
case LeUInt32: o << int8_t(BinaryConsts::I32LeU); break;
case GtSInt32: o << int8_t(BinaryConsts::I32GtS); break;
case GtUInt32: o << int8_t(BinaryConsts::I32GtU); break;
case GeSInt32: o << int8_t(BinaryConsts::I32GeS); break;
case GeUInt32: o << int8_t(BinaryConsts::I32GeU); break;
case AddInt64: o << int8_t(BinaryConsts::I64Add); break;
case SubInt64: o << int8_t(BinaryConsts::I64Sub); break;
case MulInt64: o << int8_t(BinaryConsts::I64Mul); break;
case DivSInt64: o << int8_t(BinaryConsts::I64DivS); break;
case DivUInt64: o << int8_t(BinaryConsts::I64DivU); break;
case RemSInt64: o << int8_t(BinaryConsts::I64RemS); break;
case RemUInt64: o << int8_t(BinaryConsts::I64RemU); break;
case AndInt64: o << int8_t(BinaryConsts::I64And); break;
case OrInt64: o << int8_t(BinaryConsts::I64Or); break;
case XorInt64: o << int8_t(BinaryConsts::I64Xor); break;
case ShlInt64: o << int8_t(BinaryConsts::I64Shl); break;
case ShrUInt64: o << int8_t(BinaryConsts::I64ShrU); break;
case ShrSInt64: o << int8_t(BinaryConsts::I64ShrS); break;
case RotLInt64: o << int8_t(BinaryConsts::I64RotL); break;
case RotRInt64: o << int8_t(BinaryConsts::I64RotR); break;
case EqInt64: o << int8_t(BinaryConsts::I64Eq); break;
case NeInt64: o << int8_t(BinaryConsts::I64Ne); break;
case LtSInt64: o << int8_t(BinaryConsts::I64LtS); break;
case LtUInt64: o << int8_t(BinaryConsts::I64LtU); break;
case LeSInt64: o << int8_t(BinaryConsts::I64LeS); break;
case LeUInt64: o << int8_t(BinaryConsts::I64LeU); break;
case GtSInt64: o << int8_t(BinaryConsts::I64GtS); break;
case GtUInt64: o << int8_t(BinaryConsts::I64GtU); break;
case GeSInt64: o << int8_t(BinaryConsts::I64GeS); break;
case GeUInt64: o << int8_t(BinaryConsts::I64GeU); break;
case AddFloat32: o << int8_t(BinaryConsts::F32Add); break;
case SubFloat32: o << int8_t(BinaryConsts::F32Sub); break;
case MulFloat32: o << int8_t(BinaryConsts::F32Mul); break;
case DivFloat32: o << int8_t(BinaryConsts::F32Div); break;
case CopySignFloat32: o << int8_t(BinaryConsts::F32CopySign);break;
case MinFloat32: o << int8_t(BinaryConsts::F32Min); break;
case MaxFloat32: o << int8_t(BinaryConsts::F32Max); break;
case EqFloat32: o << int8_t(BinaryConsts::F32Eq); break;
case NeFloat32: o << int8_t(BinaryConsts::F32Ne); break;
case LtFloat32: o << int8_t(BinaryConsts::F32Lt); break;
case LeFloat32: o << int8_t(BinaryConsts::F32Le); break;
case GtFloat32: o << int8_t(BinaryConsts::F32Gt); break;
case GeFloat32: o << int8_t(BinaryConsts::F32Ge); break;
case AddFloat64: o << int8_t(BinaryConsts::F64Add); break;
case SubFloat64: o << int8_t(BinaryConsts::F64Sub); break;
case MulFloat64: o << int8_t(BinaryConsts::F64Mul); break;
case DivFloat64: o << int8_t(BinaryConsts::F64Div); break;
case CopySignFloat64: o << int8_t(BinaryConsts::F64CopySign);break;
case MinFloat64: o << int8_t(BinaryConsts::F64Min); break;
case MaxFloat64: o << int8_t(BinaryConsts::F64Max); break;
case EqFloat64: o << int8_t(BinaryConsts::F64Eq); break;
case NeFloat64: o << int8_t(BinaryConsts::F64Ne); break;
case LtFloat64: o << int8_t(BinaryConsts::F64Lt); break;
case LeFloat64: o << int8_t(BinaryConsts::F64Le); break;
case GtFloat64: o << int8_t(BinaryConsts::F64Gt); break;
case GeFloat64: o << int8_t(BinaryConsts::F64Ge); break;
case InvalidBinary: WASM_UNREACHABLE();
}
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitSelect(Select* curr) {
if (debug) std::cerr << "zz node: Select" << std::endl;
visitChild(curr->ifTrue);
visitChild(curr->ifFalse);
visitChild(curr->condition);
if (curr->type == unreachable) {
emitExtraUnreachable();
return;
}
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::Select);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitReturn(Return* curr) {
if (debug) std::cerr << "zz node: Return" << std::endl;
if (curr->value) {
visitChild(curr->value);
}
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::Return);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitHost(Host* curr) {
if (debug) std::cerr << "zz node: Host" << std::endl;
switch (curr->op) {
case CurrentMemory: {
break;
}
case GrowMemory: {
visitChild(curr->operands[0]);
break;
}
}
if (justAddToStack(curr)) return;
switch (curr->op) {
case CurrentMemory: {
o << int8_t(BinaryConsts::CurrentMemory);
break;
}
case GrowMemory: {
o << int8_t(BinaryConsts::GrowMemory);
break;
}
}
o << U32LEB(0); // Reserved flags field
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitNop(Nop* curr) {
if (debug) std::cerr << "zz node: Nop" << std::endl;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::Nop);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitUnreachable(Unreachable* curr) {
if (debug) std::cerr << "zz node: Unreachable" << std::endl;
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::Unreachable);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::visitDrop(Drop* curr) {
if (debug) std::cerr << "zz node: Drop" << std::endl;
visitChild(curr->value);
if (justAddToStack(curr)) return;
o << int8_t(BinaryConsts::Drop);
}
template<StackWriterMode Mode, typename Parent>
int32_t StackWriter<Mode, Parent>::getBreakIndex(Name name) { // -1 if not found
for (int i = breakStack.size() - 1; i >= 0; i--) {
if (breakStack[i] == name) {
return breakStack.size() - 1 - i;
}
}
WASM_UNREACHABLE();
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset) {
o << U32LEB(Log2(alignment ? alignment : bytes));
o << U32LEB(offset);
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::emitExtraUnreachable() {
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(Builder(allocator).makeUnreachable()));
} else if (Mode == StackWriterMode::Binaryen2Binary) {
o << int8_t(BinaryConsts::Unreachable);
}
}
template<StackWriterMode Mode, typename Parent>
bool StackWriter<Mode, Parent>::justAddToStack(Expression* curr) {
if (Mode == StackWriterMode::Binaryen2Stack) {
stackIR.push_back(makeStackInst(curr));
return true;
}
return false;
}
template<StackWriterMode Mode, typename Parent>
void StackWriter<Mode, Parent>::finishFunctionBody() {
if (func->epilogLocation.size()) {
parent.writeDebugLocation(*func->epilogLocation.begin());
}
o << int8_t(BinaryConsts::End);
}
template<StackWriterMode Mode, typename Parent>
StackInst* StackWriter<Mode, Parent>::makeStackInst(StackInst::Op op, Expression* origin) {
auto* ret = allocator.alloc<StackInst>();
ret->op = op;
ret->origin = origin;
auto stackType = origin->type;
if (origin->is<Block>() || origin->is<Loop>() || origin->is<If>()) {
if (stackType == unreachable) {
// There are no unreachable blocks, loops, or ifs. we emit extra unreachables
// to fix that up, so that they are valid as having none type.
stackType = none;
} else if (op != StackInst::BlockEnd &&
op != StackInst::IfEnd &&
op != StackInst::LoopEnd) {
// If a concrete type is returned, we mark the end of the construct has
// having that type (as it is pushed to the value stack at that point),
// other parts are marked as none).
stackType = none;
}
}
ret->type = stackType;
return ret;
}
} // namespace wasm
#endif // wasm_stack_h