summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2019-04-18 13:13:34 -0700
committerGitHub <noreply@github.com>2019-04-18 13:13:34 -0700
commit769b92faf0447264c363aba6ce1fcec58786507f (patch)
tree41ed4953e6aaa32afed6252e814f1ee71ab1ed30
parenta9808ac6982e88262fc652d0634bf17119e1ee5f (diff)
downloadbinaryen-769b92faf0447264c363aba6ce1fcec58786507f.tar.gz
binaryen-769b92faf0447264c363aba6ce1fcec58786507f.tar.bz2
binaryen-769b92faf0447264c363aba6ce1fcec58786507f.zip
Refactor interpreter initialization to use bulk memory (#2025)
This corresponds to changes made to the initialization procedure in the spec. It also removes all the heavy initialization work from the external interface of the interpreter, which is a nice encapsulation win. Implementation of the interpretation of the remaining bulk memory operations and more rigorous tests of that interpretation will come in a follow-up PR.
-rw-r--r--src/shell-interface.h27
-rw-r--r--src/tools/wasm-ctor-eval.cpp15
-rw-r--r--src/wasm-interpreter.h600
3 files changed, 357 insertions, 285 deletions
diff --git a/src/shell-interface.h b/src/shell-interface.h
index 6c6961541..4cb5f5349 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -95,30 +95,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
void init(Module& wasm, ModuleInstance& instance) override {
memory.resize(wasm.memory.initial * wasm::Memory::kPageSize);
- // apply memory segments
- for (auto& segment : wasm.memory.segments) {
- if (segment.isPassive) {
- continue;
- }
- Address offset = (uint32_t)ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
- if (offset + segment.data.size() > wasm.memory.initial * wasm::Memory::kPageSize) {
- trap("invalid offset when initializing memory");
- }
- for (size_t i = 0; i != segment.data.size(); ++i) {
- memory.set(offset + i, segment.data[i]);
- }
- }
-
table.resize(wasm.table.initial);
- for (auto& segment : wasm.table.segments) {
- Address offset = (uint32_t)ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
- if (offset + segment.data.size() > wasm.table.initial) {
- trap("invalid offset when initializing table");
- }
- for (size_t i = 0; i != segment.data.size(); ++i) {
- table[offset + i] = segment.data[i];
- }
- }
}
void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {
@@ -198,6 +175,10 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
memory.set<std::array<uint8_t, 16>>(addr, value);
}
+ void tableStore(Address addr, Name entry) override {
+ table[addr] = entry;
+ }
+
void growMemory(Address /*oldSize*/, Address newSize) override {
memory.resize(newSize);
}
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 7509eb84f..420ec06ad 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -161,11 +161,6 @@ public:
auto total = STACK_START + STACK_SIZE;
memorySize = total / Memory::kPageSize;
}
-
- // flatten memory into a single segment, return true if successful
- bool flattenMemory() {
- return MemoryUtils::flatten(wasm.memory);
- }
};
struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
@@ -257,6 +252,9 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
void store32(Address addr, int32_t value) override { doStore<int32_t>(addr, value); }
void store64(Address addr, int64_t value) override { doStore<int64_t>(addr, value); }
+ // called during initialization, but we don't keep track of a table
+ void tableStore(Address addr, Name value) override { }
+
void growMemory(Address /*oldSize*/, Address newSize) override {
throw FailToEvalException("grow memory");
}
@@ -291,6 +289,7 @@ private:
)
);
}
+ // memory should already have been flattened
assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() == 0);
auto max = address + sizeof(T);
auto& data = wasm->memory.segments[0].data;
@@ -318,12 +317,12 @@ private:
void evalCtors(Module& wasm, std::vector<std::string> ctors) {
CtorEvalExternalInterface interface;
try {
- // create an instance for evalling
- EvallingModuleInstance instance(wasm, &interface);
// flatten memory, so we do not depend on the layout of data segments
- if (!instance.flattenMemory()) {
+ if (!MemoryUtils::flatten(wasm.memory)) {
Fatal() << " ...stopping since could not flatten memory\n";
}
+ // create an instance for evalling
+ EvallingModuleInstance instance(wasm, &interface);
// set up the stack area and other environment details
instance.setupEnvironment();
// we should not add new globals from here on; as a result, using
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index bd6765770..fcdb44c42 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -839,6 +839,8 @@ public:
virtual void store32(Address addr, int32_t value) { WASM_UNREACHABLE(); }
virtual void store64(Address addr, int64_t value) { WASM_UNREACHABLE(); }
virtual void store128(Address addr, const std::array<uint8_t, 16>&) { WASM_UNREACHABLE(); }
+
+ virtual void tableStore(Address addr, Name entry) { WASM_UNREACHABLE(); }
};
SubType* self() {
@@ -859,8 +861,13 @@ public:
ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) {
globals[global->name] = ConstantExpressionRunner<GlobalManager>(globals).visit(global->init).value;
});
+
// initialize the rest of the external interface
externalInterface->init(wasm, *self());
+
+ initializeTableContents();
+ initializeMemoryContents();
+
// run start, if present
if (wasm.start.is()) {
LiteralList arguments;
@@ -906,286 +913,371 @@ private:
// stack traces.
std::vector<Name> functionStack;
-public:
- // Call a function, starting an invocation.
- Literal callFunction(Name name, const LiteralList& arguments) {
- // if the last call ended in a jump up the stack, it might have left stuff for us to clean up here
- callDepth = 0;
- functionStack.clear();
- return callFunctionInternal(name, arguments);
+ std::unordered_set<size_t> droppedSegments;
+
+ void initializeTableContents() {
+ for (auto& segment : wasm.table.segments) {
+ Address offset = (uint32_t)ConstantExpressionRunner<GlobalManager>(globals).visit(segment.offset).value.geti32();
+ if (offset + segment.data.size() > wasm.table.initial) {
+ externalInterface->trap("invalid offset when initializing table");
+ }
+ for (size_t i = 0; i != segment.data.size(); ++i) {
+ externalInterface->tableStore(offset + i, segment.data[i]);
+ }
+ }
}
- // Internal function call. Must be public so that callTable implementations can use it (refactor?)
- Literal callFunctionInternal(Name name, const LiteralList& arguments) {
+ void initializeMemoryContents() {
+ // no way to create a Block without an ArenaAllocator, so use a builder
+ // instead of creating it locally.
+ Builder builder(wasm);
- class FunctionScope {
- public:
- std::vector<Literal> locals;
- Function* function;
-
- FunctionScope(Function* function, const LiteralList& arguments)
- : function(function) {
- if (function->params.size() != arguments.size()) {
- std::cerr << "Function `" << function->name << "` expects "
- << function->params.size() << " parameters, got "
- << arguments.size() << " arguments." << std::endl;
- WASM_UNREACHABLE();
- }
- locals.resize(function->getNumLocals());
- for (size_t i = 0; i < function->getNumLocals(); i++) {
- if (i < arguments.size()) {
- assert(function->isParam(i));
- if (function->params[i] != arguments[i].type) {
- std::cerr << "Function `" << function->name << "` expects type "
- << printType(function->params[i])
- << " for parameter " << i << ", got "
- << printType(arguments[i].type) << "." << std::endl;
- WASM_UNREACHABLE();
- }
- locals[i] = arguments[i];
- } else {
- assert(function->isVar(i));
- locals[i].type = function->getLocalType(i);
- }
- }
- }
- };
+ Const offset;
+ offset.value = Literal(uint32_t(0));
+ offset.finalize();
- // Executes expressions with concrete runtime info, the function and module at runtime
- class RuntimeExpressionRunner : public ExpressionRunner<RuntimeExpressionRunner> {
- ModuleInstanceBase& instance;
- FunctionScope& scope;
-
- public:
- RuntimeExpressionRunner(ModuleInstanceBase& instance, FunctionScope& scope) : instance(instance), scope(scope) {}
-
- Flow generateArguments(const ExpressionList& operands, LiteralList& arguments) {
- NOTE_ENTER_("generateArguments");
- arguments.reserve(operands.size());
- for (auto expression : operands) {
- Flow flow = this->visit(expression);
- if (flow.breaking()) return flow;
- NOTE_EVAL1(flow.value);
- arguments.push_back(flow.value);
- }
- return Flow();
+ // apply active memory segments
+ for (size_t i = 0, e = wasm.memory.segments.size(); i < e; ++i) {
+ Memory::Segment& segment = wasm.memory.segments[i];
+ if (segment.isPassive) {
+ continue;
}
- Flow visitCall(Call *curr) {
- NOTE_ENTER("Call");
- NOTE_NAME(curr->target);
- LiteralList arguments;
- Flow flow = generateArguments(curr->operands, arguments);
- if (flow.breaking()) return flow;
- auto* func = instance.wasm.getFunction(curr->target);
- Flow ret;
- if (func->imported()) {
- ret = instance.externalInterface->callImport(func, arguments);
+ Const size;
+ size.value = Literal(uint32_t(segment.data.size()));
+ size.finalize();
+
+ MemoryInit init;
+ init.segment = i;
+ init.dest = segment.offset;
+ init.offset = &offset;
+ init.size = &size;
+ init.finalize();
+
+ DataDrop drop;
+ drop.segment = segment.index;
+ drop.finalize();
+
+ Function initializer;
+ initializer.body = builder.blockify(&init, &drop);
+
+ FunctionScope scope(&initializer, {});
+
+ RuntimeExpressionRunner(*this, scope).visit(&init);
+ }
+ }
+
+ class FunctionScope {
+ public:
+ std::vector<Literal> locals;
+ Function* function;
+
+ FunctionScope(Function* function, const LiteralList& arguments)
+ : function(function) {
+ if (function->params.size() != arguments.size()) {
+ std::cerr << "Function `" << function->name << "` expects "
+ << function->params.size() << " parameters, got "
+ << arguments.size() << " arguments." << std::endl;
+ WASM_UNREACHABLE();
+ }
+ locals.resize(function->getNumLocals());
+ for (size_t i = 0; i < function->getNumLocals(); i++) {
+ if (i < arguments.size()) {
+ assert(function->isParam(i));
+ if (function->params[i] != arguments[i].type) {
+ std::cerr << "Function `" << function->name << "` expects type "
+ << printType(function->params[i])
+ << " for parameter " << i << ", got "
+ << printType(arguments[i].type) << "." << std::endl;
+ WASM_UNREACHABLE();
+ }
+ locals[i] = arguments[i];
} else {
- ret = instance.callFunctionInternal(curr->target, arguments);
+ assert(function->isVar(i));
+ locals[i].type = function->getLocalType(i);
}
-#ifdef WASM_INTERPRETER_DEBUG
- std::cout << "(returned to " << scope.function->name << ")\n";
-#endif
- return ret;
- }
- Flow visitCallIndirect(CallIndirect *curr) {
- NOTE_ENTER("CallIndirect");
- LiteralList arguments;
- Flow flow = generateArguments(curr->operands, arguments);
- if (flow.breaking()) return flow;
- Flow target = this->visit(curr->target);
- if (target.breaking()) return target;
- Index index = target.value.geti32();
- return instance.externalInterface->callTable(index, arguments, curr->type, *instance.self());
}
+ }
+ };
- Flow visitGetLocal(GetLocal *curr) {
- NOTE_ENTER("GetLocal");
- auto index = curr->index;
- NOTE_EVAL1(index);
- NOTE_EVAL1(scope.locals[index]);
- return scope.locals[index];
- }
- Flow visitSetLocal(SetLocal *curr) {
- NOTE_ENTER("SetLocal");
- auto index = curr->index;
- Flow flow = this->visit(curr->value);
+ // Executes expressions with concrete runtime info, the function and module at runtime
+ class RuntimeExpressionRunner : public ExpressionRunner<RuntimeExpressionRunner> {
+ ModuleInstanceBase& instance;
+ FunctionScope& scope;
+
+ public:
+ RuntimeExpressionRunner(ModuleInstanceBase& instance, FunctionScope& scope) : instance(instance), scope(scope) {}
+
+ Flow generateArguments(const ExpressionList& operands, LiteralList& arguments) {
+ NOTE_ENTER_("generateArguments");
+ arguments.reserve(operands.size());
+ for (auto expression : operands) {
+ Flow flow = this->visit(expression);
if (flow.breaking()) return flow;
- NOTE_EVAL1(index);
NOTE_EVAL1(flow.value);
- assert(curr->isTee() ? flow.value.type == curr->type : true);
- scope.locals[index] = flow.value;
- return curr->isTee() ? flow : Flow();
+ arguments.push_back(flow.value);
}
+ return Flow();
+ }
- Flow visitGetGlobal(GetGlobal *curr) {
- NOTE_ENTER("GetGlobal");
- auto name = curr->name;
- NOTE_EVAL1(name);
- assert(instance.globals.find(name) != instance.globals.end());
- NOTE_EVAL1(instance.globals[name]);
- return instance.globals[name];
- }
- Flow visitSetGlobal(SetGlobal *curr) {
- NOTE_ENTER("SetGlobal");
- auto name = curr->name;
- Flow flow = this->visit(curr->value);
- if (flow.breaking()) return flow;
- NOTE_EVAL1(name);
- NOTE_EVAL1(flow.value);
- instance.globals[name] = flow.value;
- return Flow();
+ Flow visitCall(Call *curr) {
+ NOTE_ENTER("Call");
+ NOTE_NAME(curr->target);
+ LiteralList arguments;
+ Flow flow = generateArguments(curr->operands, arguments);
+ if (flow.breaking()) return flow;
+ auto* func = instance.wasm.getFunction(curr->target);
+ Flow ret;
+ if (func->imported()) {
+ ret = instance.externalInterface->callImport(func, arguments);
+ } else {
+ ret = instance.callFunctionInternal(curr->target, arguments);
}
+#ifdef WASM_INTERPRETER_DEBUG
+ std::cout << "(returned to " << scope.function->name << ")\n";
+#endif
+ return ret;
+ }
+ Flow visitCallIndirect(CallIndirect *curr) {
+ NOTE_ENTER("CallIndirect");
+ LiteralList arguments;
+ Flow flow = generateArguments(curr->operands, arguments);
+ if (flow.breaking()) return flow;
+ Flow target = this->visit(curr->target);
+ if (target.breaking()) return target;
+ Index index = target.value.geti32();
+ return instance.externalInterface->callTable(index, arguments, curr->type, *instance.self());
+ }
- Flow visitLoad(Load *curr) {
- NOTE_ENTER("Load");
- Flow flow = this->visit(curr->ptr);
- if (flow.breaking()) return flow;
- NOTE_EVAL1(flow);
- auto addr = instance.getFinalAddress(curr, flow.value);
- auto ret = instance.externalInterface->load(curr, addr);
- NOTE_EVAL1(addr);
- NOTE_EVAL1(ret);
- return ret;
- }
- Flow visitStore(Store *curr) {
- NOTE_ENTER("Store");
- Flow ptr = this->visit(curr->ptr);
- if (ptr.breaking()) return ptr;
- Flow value = this->visit(curr->value);
- if (value.breaking()) return value;
- auto addr = instance.getFinalAddress(curr, ptr.value);
- NOTE_EVAL1(addr);
- NOTE_EVAL1(value);
- instance.externalInterface->store(curr, addr, value.value);
- return Flow();
- }
+ Flow visitGetLocal(GetLocal *curr) {
+ NOTE_ENTER("GetLocal");
+ auto index = curr->index;
+ NOTE_EVAL1(index);
+ NOTE_EVAL1(scope.locals[index]);
+ return scope.locals[index];
+ }
+ Flow visitSetLocal(SetLocal *curr) {
+ NOTE_ENTER("SetLocal");
+ auto index = curr->index;
+ Flow flow = this->visit(curr->value);
+ if (flow.breaking()) return flow;
+ NOTE_EVAL1(index);
+ NOTE_EVAL1(flow.value);
+ assert(curr->isTee() ? flow.value.type == curr->type : true);
+ scope.locals[index] = flow.value;
+ return curr->isTee() ? flow : Flow();
+ }
- Flow visitAtomicRMW(AtomicRMW *curr) {
- NOTE_ENTER("AtomicRMW");
- Flow ptr = this->visit(curr->ptr);
- if (ptr.breaking()) return ptr;
- auto value = this->visit(curr->value);
- if (value.breaking()) return value;
- NOTE_EVAL1(ptr);
- auto addr = instance.getFinalAddress(curr, ptr.value);
- NOTE_EVAL1(addr);
- NOTE_EVAL1(value);
- auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type);
- NOTE_EVAL1(loaded);
- auto computed = value.value;
- switch (curr->op) {
- case Add: computed = computed.add(value.value); break;
- case Sub: computed = computed.sub(value.value); break;
- case And: computed = computed.and_(value.value); break;
- case Or: computed = computed.or_(value.value); break;
- case Xor: computed = computed.xor_(value.value); break;
- case Xchg: computed = value.value; break;
- }
- instance.doAtomicStore(addr, curr->bytes, computed);
- return loaded;
- }
- Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) {
- NOTE_ENTER("AtomicCmpxchg");
- Flow ptr = this->visit(curr->ptr);
- if (ptr.breaking()) return ptr;
- NOTE_EVAL1(ptr);
- auto expected = this->visit(curr->expected);
- if (expected.breaking()) return expected;
- auto replacement = this->visit(curr->replacement);
- if (replacement.breaking()) return replacement;
- auto addr = instance.getFinalAddress(curr, ptr.value);
- NOTE_EVAL1(addr);
- NOTE_EVAL1(expected);
- NOTE_EVAL1(replacement);
- auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type);
- NOTE_EVAL1(loaded);
- if (loaded == expected.value) {
- instance.doAtomicStore(addr, curr->bytes, replacement.value);
- }
- return loaded;
+ Flow visitGetGlobal(GetGlobal *curr) {
+ NOTE_ENTER("GetGlobal");
+ auto name = curr->name;
+ NOTE_EVAL1(name);
+ assert(instance.globals.find(name) != instance.globals.end());
+ NOTE_EVAL1(instance.globals[name]);
+ return instance.globals[name];
+ }
+ Flow visitSetGlobal(SetGlobal *curr) {
+ NOTE_ENTER("SetGlobal");
+ auto name = curr->name;
+ Flow flow = this->visit(curr->value);
+ if (flow.breaking()) return flow;
+ NOTE_EVAL1(name);
+ NOTE_EVAL1(flow.value);
+ instance.globals[name] = flow.value;
+ return Flow();
+ }
+
+ Flow visitLoad(Load *curr) {
+ NOTE_ENTER("Load");
+ Flow flow = this->visit(curr->ptr);
+ if (flow.breaking()) return flow;
+ NOTE_EVAL1(flow);
+ auto addr = instance.getFinalAddress(curr, flow.value);
+ auto ret = instance.externalInterface->load(curr, addr);
+ NOTE_EVAL1(addr);
+ NOTE_EVAL1(ret);
+ return ret;
+ }
+ Flow visitStore(Store *curr) {
+ NOTE_ENTER("Store");
+ Flow ptr = this->visit(curr->ptr);
+ if (ptr.breaking()) return ptr;
+ Flow value = this->visit(curr->value);
+ if (value.breaking()) return value;
+ auto addr = instance.getFinalAddress(curr, ptr.value);
+ NOTE_EVAL1(addr);
+ NOTE_EVAL1(value);
+ instance.externalInterface->store(curr, addr, value.value);
+ return Flow();
+ }
+
+ Flow visitAtomicRMW(AtomicRMW *curr) {
+ NOTE_ENTER("AtomicRMW");
+ Flow ptr = this->visit(curr->ptr);
+ if (ptr.breaking()) return ptr;
+ auto value = this->visit(curr->value);
+ if (value.breaking()) return value;
+ NOTE_EVAL1(ptr);
+ auto addr = instance.getFinalAddress(curr, ptr.value);
+ NOTE_EVAL1(addr);
+ NOTE_EVAL1(value);
+ auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type);
+ NOTE_EVAL1(loaded);
+ auto computed = value.value;
+ switch (curr->op) {
+ case Add: computed = computed.add(value.value); break;
+ case Sub: computed = computed.sub(value.value); break;
+ case And: computed = computed.and_(value.value); break;
+ case Or: computed = computed.or_(value.value); break;
+ case Xor: computed = computed.xor_(value.value); break;
+ case Xchg: computed = value.value; break;
}
- Flow visitAtomicWait(AtomicWait *curr) {
- NOTE_ENTER("AtomicWait");
- Flow ptr = this->visit(curr->ptr);
- if (ptr.breaking()) return ptr;
- NOTE_EVAL1(ptr);
- auto expected = this->visit(curr->expected);
- NOTE_EVAL1(expected);
- if (expected.breaking()) return expected;
- auto timeout = this->visit(curr->timeout);
- NOTE_EVAL1(timeout);
- if (timeout.breaking()) return timeout;
- auto bytes = getTypeSize(curr->expectedType);
- auto addr = instance.getFinalAddress(ptr.value, bytes);
- auto loaded = instance.doAtomicLoad(addr, bytes, curr->expectedType);
- NOTE_EVAL1(loaded);
- if (loaded != expected.value) {
- return Literal(int32_t(1)); // not equal
- }
- // TODO: add threads support!
- // for now, just assume we are woken up
- return Literal(int32_t(0)); // woken up
+ instance.doAtomicStore(addr, curr->bytes, computed);
+ return loaded;
+ }
+ Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) {
+ NOTE_ENTER("AtomicCmpxchg");
+ Flow ptr = this->visit(curr->ptr);
+ if (ptr.breaking()) return ptr;
+ NOTE_EVAL1(ptr);
+ auto expected = this->visit(curr->expected);
+ if (expected.breaking()) return expected;
+ auto replacement = this->visit(curr->replacement);
+ if (replacement.breaking()) return replacement;
+ auto addr = instance.getFinalAddress(curr, ptr.value);
+ NOTE_EVAL1(addr);
+ NOTE_EVAL1(expected);
+ NOTE_EVAL1(replacement);
+ auto loaded = instance.doAtomicLoad(addr, curr->bytes, curr->type);
+ NOTE_EVAL1(loaded);
+ if (loaded == expected.value) {
+ instance.doAtomicStore(addr, curr->bytes, replacement.value);
}
- Flow visitAtomicNotify(AtomicNotify *curr) {
- NOTE_ENTER("AtomicNotify");
- Flow ptr = this->visit(curr->ptr);
- if (ptr.breaking()) return ptr;
- NOTE_EVAL1(ptr);
- auto count = this->visit(curr->notifyCount);
- NOTE_EVAL1(count);
- if (count.breaking()) return count;
- // TODO: add threads support!
- return Literal(int32_t(0)); // none woken up
+ return loaded;
+ }
+ Flow visitAtomicWait(AtomicWait *curr) {
+ NOTE_ENTER("AtomicWait");
+ Flow ptr = this->visit(curr->ptr);
+ if (ptr.breaking()) return ptr;
+ NOTE_EVAL1(ptr);
+ auto expected = this->visit(curr->expected);
+ NOTE_EVAL1(expected);
+ if (expected.breaking()) return expected;
+ auto timeout = this->visit(curr->timeout);
+ NOTE_EVAL1(timeout);
+ if (timeout.breaking()) return timeout;
+ auto bytes = getTypeSize(curr->expectedType);
+ auto addr = instance.getFinalAddress(ptr.value, bytes);
+ auto loaded = instance.doAtomicLoad(addr, bytes, curr->expectedType);
+ NOTE_EVAL1(loaded);
+ if (loaded != expected.value) {
+ return Literal(int32_t(1)); // not equal
}
- Flow visitHost(Host *curr) {
- NOTE_ENTER("Host");
- switch (curr->op) {
- case CurrentMemory: return Literal(int32_t(instance.memorySize));
- case GrowMemory: {
- auto fail = Literal(int32_t(-1));
- Flow flow = this->visit(curr->operands[0]);
- if (flow.breaking()) return flow;
- int32_t ret = instance.memorySize;
- uint32_t delta = flow.value.geti32();
- if (delta > uint32_t(-1) /Memory::kPageSize) return fail;
- if (instance.memorySize >= uint32_t(-1) - delta) return fail;
- uint32_t newSize = instance.memorySize + delta;
- if (newSize > instance.wasm.memory.max) return fail;
- instance.externalInterface->growMemory(instance.memorySize * Memory::kPageSize, newSize * Memory::kPageSize);
- instance.memorySize = newSize;
- return Literal(int32_t(ret));
- }
- }
- WASM_UNREACHABLE();
+ // TODO: add threads support!
+ // for now, just assume we are woken up
+ return Literal(int32_t(0)); // woken up
+ }
+ Flow visitAtomicNotify(AtomicNotify *curr) {
+ NOTE_ENTER("AtomicNotify");
+ Flow ptr = this->visit(curr->ptr);
+ if (ptr.breaking()) return ptr;
+ NOTE_EVAL1(ptr);
+ auto count = this->visit(curr->notifyCount);
+ NOTE_EVAL1(count);
+ if (count.breaking()) return count;
+ // TODO: add threads support!
+ return Literal(int32_t(0)); // none woken up
+ }
+ Flow visitHost(Host *curr) {
+ NOTE_ENTER("Host");
+ switch (curr->op) {
+ case CurrentMemory: return Literal(int32_t(instance.memorySize));
+ case GrowMemory: {
+ auto fail = Literal(int32_t(-1));
+ Flow flow = this->visit(curr->operands[0]);
+ if (flow.breaking()) return flow;
+ int32_t ret = instance.memorySize;
+ uint32_t delta = flow.value.geti32();
+ if (delta > uint32_t(-1) /Memory::kPageSize) return fail;
+ if (instance.memorySize >= uint32_t(-1) - delta) return fail;
+ uint32_t newSize = instance.memorySize + delta;
+ if (newSize > instance.wasm.memory.max) return fail;
+ instance.externalInterface->growMemory(instance.memorySize * Memory::kPageSize, newSize * Memory::kPageSize);
+ instance.memorySize = newSize;
+ return Literal(int32_t(ret));
}
- Flow visitMemoryInit(MemoryInit *curr) {
- NOTE_ENTER("MemoryInit");
- // TODO(tlively): implement me
- return {};
}
- Flow visitDataDrop(DataDrop *curr) {
- NOTE_ENTER("DataDrop");
- // TODO(tlively): implement me
- return {};
+ WASM_UNREACHABLE();
+ }
+ Flow visitMemoryInit(MemoryInit *curr) {
+ NOTE_ENTER("MemoryInit");
+ Flow dest = this->visit(curr->dest);
+ if (dest.breaking()) return dest;
+ Flow offset = this->visit(curr->offset);
+ if (offset.breaking()) return offset;
+ Flow size = this->visit(curr->size);
+ if (size.breaking()) return size;
+ NOTE_EVAL1(dest);
+ NOTE_EVAL1(offset);
+ NOTE_EVAL1(size);
+
+ assert(curr->segment < instance.wasm.memory.segments.size());
+ Memory::Segment& segment = instance.wasm.memory.segments[curr->segment];
+
+ if (instance.droppedSegments.count(curr->segment)) {
+ trap("memory.init of dropped segment");
}
- Flow visitMemoryCopy(MemoryCopy *curr) {
- NOTE_ENTER("MemoryCopy");
- // TODO(tlively): implement me
- return {};
+
+ size_t destVal(dest.value.geti32());
+ size_t offsetVal(offset.value.geti32());
+ size_t sizeVal(size.value.geti32());
+ for (size_t i = 0; i < sizeVal; ++i) {
+ if (offsetVal + i >= segment.data.size()) {
+ trap("out of bounds segment access in memory.init");
+ }
+ Literal addr = Literal(uint32_t(destVal + i));
+ instance.externalInterface->store8(
+ instance.getFinalAddress(addr, 1),
+ segment.data[offsetVal + i]
+ );
}
- Flow visitMemoryFill(MemoryFill *curr) {
- NOTE_ENTER("MemoryFill");
- // TODO(tlively): implement me
- return {};
+ return {};
+ }
+ Flow visitDataDrop(DataDrop *curr) {
+ NOTE_ENTER("DataDrop");
+ if (instance.droppedSegments.count(curr->segment)) {
+ trap("data.drop of dropped segment");
}
+ instance.droppedSegments.insert(curr->segment);
+ return {};
+ }
+ Flow visitMemoryCopy(MemoryCopy *curr) {
+ NOTE_ENTER("MemoryCopy");
+ // TODO(tlively): implement me
+ return {};
+ }
+ Flow visitMemoryFill(MemoryFill *curr) {
+ NOTE_ENTER("MemoryFill");
+ // TODO(tlively): implement me
+ return {};
+ }
- void trap(const char* why) override {
- instance.externalInterface->trap(why);
- }
- };
+ void trap(const char* why) override {
+ instance.externalInterface->trap(why);
+ }
+ };
+public:
+ // Call a function, starting an invocation.
+ Literal callFunction(Name name, const LiteralList& arguments) {
+ // if the last call ended in a jump up the stack, it might have left stuff for us to clean up here
+ callDepth = 0;
+ functionStack.clear();
+ return callFunctionInternal(name, arguments);
+ }
+
+ // Internal function call. Must be public so that callTable implementations can use it (refactor?)
+ Literal callFunctionInternal(Name name, const LiteralList& arguments) {
if (callDepth > maxCallDepth) externalInterface->trap("stack limit");
auto previousCallDepth = callDepth;
callDepth++;
@@ -1294,7 +1386,7 @@ protected:
};
// The default ModuleInstance uses a trivial global manager
-typedef std::map<Name, Literal> TrivialGlobalManager;
+using TrivialGlobalManager = std::map<Name, Literal>;
class ModuleInstance : public ModuleInstanceBase<TrivialGlobalManager, ModuleInstance> {
public:
ModuleInstance(Module& wasm, ExternalInterface* externalInterface) : ModuleInstanceBase(wasm, externalInterface) {}