blob: 8007f011dcc795059693eff8d11e53a8d46806d0 [file] [log] [blame] [edit]
/*
* Copyright 2025 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.
*/
#include "interpreter/interpreter.h"
#include "interpreter/expression-iterator.h"
#include "interpreter/store.h"
#include "wasm-traversal.h"
namespace wasm {
using namespace interpreter;
// Provides access to the interpreter's private store.
class InterpreterImpl {
public:
static WasmStore& getStore(Interpreter& interpreter) {
return interpreter.store;
}
};
namespace {
struct Branch {
Name label;
};
// TODO: Handle other forms of control flow.
struct Flow : std::variant<std::monostate, Branch> {
operator bool() { return !std::get_if<std::monostate>(this); }
};
struct ExpressionInterpreter : OverriddenVisitor<ExpressionInterpreter, Flow> {
Interpreter& parent;
ExpressionInterpreter(Interpreter& parent) : parent(parent) {}
WasmStore& store() { return InterpreterImpl::getStore(parent); }
Frame& frame() { return store().callStack.back(); }
Instance& instance() { return frame().instance; }
void push(Literal val) { store().push(val); }
Literal pop() { return store().pop(); }
Flow visitNop(Nop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitBlock(Block* curr) { return {}; }
Flow visitIf(If* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitLoop(Loop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitBreak(Break* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSwitch(Switch* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitCall(Call* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitCallIndirect(CallIndirect* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitLocalGet(LocalGet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitLocalSet(LocalSet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitGlobalGet(GlobalGet* curr) {
push(instance().globalValues[curr->name]);
return {};
}
Flow visitGlobalSet(GlobalSet* curr) {
instance().globalValues[curr->name] = pop();
return {};
}
Flow visitLoad(Load* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStore(Store* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitAtomicRMW(AtomicRMW* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitAtomicCmpxchg(AtomicCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitAtomicWait(AtomicWait* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitAtomicNotify(AtomicNotify* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitAtomicFence(AtomicFence* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitPause(Pause* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDExtract(SIMDExtract* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDReplace(SIMDReplace* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDShuffle(SIMDShuffle* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDTernary(SIMDTernary* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDShift(SIMDShift* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDLoad(SIMDLoad* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
WASM_UNREACHABLE("TODO");
}
Flow visitMemoryInit(MemoryInit* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitDataDrop(DataDrop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitMemoryCopy(MemoryCopy* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitMemoryFill(MemoryFill* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitConst(Const* curr) {
push(curr->value);
return {};
}
Flow visitUnary(Unary* curr) {
auto value = pop();
switch (curr->op) {
case SqrtFloat32:
case SqrtFloat64:
push(value.sqrt());
return {};
case CeilFloat32:
case CeilFloat64:
push(value.ceil());
return {};
case FloorFloat32:
case FloorFloat64:
push(value.floor());
return {};
case TruncFloat32:
case TruncFloat64:
push(value.trunc());
return {};
case NearestFloat32:
case NearestFloat64:
push(value.nearbyint());
return {};
default:
WASM_UNREACHABLE("TODO");
}
}
Flow visitBinary(Binary* curr) {
auto rhs = pop();
auto lhs = pop();
// TODO: add support for all operations.
switch (curr->op) {
case AddInt32:
case AddInt64:
case AddFloat32:
case AddFloat64:
push(lhs.add(rhs));
return {};
case SubInt32:
case SubInt64:
case SubFloat32:
case SubFloat64:
push(lhs.sub(rhs));
return {};
case MulInt32:
case MulInt64:
case MulFloat32:
case MulFloat64:
push(lhs.mul(rhs));
return {};
case DivFloat32:
case DivFloat64:
push(lhs.div(rhs));
return {};
case AndInt32:
case AndInt64:
push(lhs.and_(rhs));
return {};
case OrInt32:
case OrInt64:
push(lhs.or_(rhs));
return {};
case XorInt32:
case XorInt64:
push(lhs.xor_(rhs));
return {};
case ShlInt32:
case ShlInt64:
push(lhs.shl(rhs));
return {};
case ShrUInt32:
case ShrUInt64:
push(lhs.shrU(rhs));
return {};
case ShrSInt32:
case ShrSInt64:
push(lhs.shrS(rhs));
return {};
case RotLInt32:
case RotLInt64:
push(lhs.rotL(rhs));
return {};
case RotRInt32:
case RotRInt64:
push(lhs.rotR(rhs));
return {};
case EqInt32:
case EqInt64:
case EqFloat32:
case EqFloat64:
push(lhs.eq(rhs));
return {};
case LtSInt32:
case LtSInt64:
push(lhs.ltS(rhs));
return {};
case LtUInt32:
case LtUInt64:
push(lhs.ltU(rhs));
return {};
case GtSInt32:
case GtSInt64:
push(lhs.gtS(rhs));
return {};
case GtUInt32:
case GtUInt64:
push(lhs.gtU(rhs));
return {};
case MinFloat32:
case MinFloat64:
push(lhs.min(rhs));
return {};
case MaxFloat32:
case MaxFloat64:
push(lhs.max(rhs));
return {};
default:
WASM_UNREACHABLE("TODO");
}
}
Flow visitSelect(Select* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitDrop(Drop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitReturn(Return* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitMemorySize(MemorySize* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitMemoryGrow(MemoryGrow* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitUnreachable(Unreachable* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitPop(Pop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefNull(RefNull* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefIsNull(RefIsNull* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefFunc(RefFunc* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefEq(RefEq* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableCopy(TableCopy* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTableInit(TableInit* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitElemDrop(ElemDrop* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTry(Try* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTryTable(TryTable* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitThrow(Throw* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitThrowRef(ThrowRef* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTupleMake(TupleMake* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitTupleExtract(TupleExtract* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefI31(RefI31* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitI31Get(I31Get* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitCallRef(CallRef* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefTest(RefTest* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefCast(RefCast* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefGetDesc(RefGetDesc* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitBrOn(BrOn* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructNew(StructNew* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructGet(StructGet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructSet(StructSet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructRMW(StructRMW* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStructCmpxchg(StructCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNew(ArrayNew* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNewData(ArrayNewData* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNewElem(ArrayNewElem* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayNewFixed(ArrayNewFixed* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayGet(ArrayGet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArraySet(ArraySet* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayLen(ArrayLen* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayCopy(ArrayCopy* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayFill(ArrayFill* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayInitData(ArrayInitData* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayInitElem(ArrayInitElem* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayRMW(ArrayRMW* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitArrayCmpxchg(ArrayCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitRefAs(RefAs* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringNew(StringNew* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringConst(StringConst* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringMeasure(StringMeasure* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringEncode(StringEncode* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringConcat(StringConcat* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringEq(StringEq* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringTest(StringTest* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringWTF16Get(StringWTF16Get* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStringSliceWTF(StringSliceWTF* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitContNew(ContNew* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitContBind(ContBind* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitSuspend(Suspend* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); }
Flow visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); }
};
} // anonymous namespace
Result<> Interpreter::addInstance(std::shared_ptr<Module> wasm) {
return instantiate(store.instances.emplace_back(wasm));
}
Result<> Interpreter::instantiate(Instance& instance) {
for (auto& global : instance.wasm->globals) {
store.callStack.emplace_back(instance, ExpressionIterator(global->init));
auto results = run();
assert(results.size() == 1);
instance.globalValues[global->name] = results[0];
}
return Ok{};
}
// This is a temporary convenience while stil using gTests to validate this
// interpreter. Once spec tests can run, this shall be deleted.
std::vector<Literal> Interpreter::runTest(Expression* root) {
static std::shared_ptr<wasm::Module> dummyModule = std::make_shared<Module>();
if (store.instances.empty()) {
auto result = addInstance(dummyModule);
}
store.callStack.emplace_back(store.instances.back(),
ExpressionIterator(root));
return run();
}
std::vector<Literal> Interpreter::run() {
ExpressionInterpreter interpreter(*this);
while (auto& it = store.callStack.back().exprs) {
if (auto flow = interpreter.visit(*it)) {
// TODO: Handle control flow transfers.
} else {
++it;
}
}
auto valueStack = store.callStack.back().valueStack;
store.callStack.pop_back();
return valueStack;
}
} // namespace wasm