Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions src/shell-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
wasm, [&](Table* table) { tables[table->name].resize(table->initial); });
}

void importGlobals(std::map<Name, Literals>& globals, Module& wasm) override {
ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
auto inst = getImportInstance(import);
auto* exportedGlobal = inst->wasm.getExportOrNull(import->base);
if (!exportedGlobal || exportedGlobal->kind != ExternalKind::Global) {
Fatal() << "importGlobals: unknown import: " << import->module.str
<< "." << import->name.str;
}
globals[import->name] = inst->globals[*exportedGlobal->getInternalName()];
});
}

Literal getImportedFunction(Function* import) override {
// TODO: We should perhaps restrict the types with which the well-known
// functions can be imported.
Expand Down
67 changes: 28 additions & 39 deletions src/tools/wasm-ctor-eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,32 @@ bool isNullableAndMutable(Expression* ref, Index fieldIndex) {
// the output.
#define RECOMMENDATION "\n recommendation: "

class EvallingModuleRunner;

class EvallingImportResolver : public ImportResolver {
public:
EvallingImportResolver() {}

std::optional<Literals*> getGlobal(QualifiedName name,
Type type) const override {
auto [it, _] = stubLiterals.try_emplace(name, Literals({Literal(type)}));
return &it->second;
}

private:
mutable std::map<QualifiedName, Literals> stubLiterals;
};

class EvallingModuleRunner : public ModuleRunnerBase<EvallingModuleRunner> {
public:
EvallingModuleRunner(
Module& wasm,
ExternalInterface* externalInterface,
std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ = {})
: ModuleRunnerBase(wasm, externalInterface, linkedInstances_) {}
: ModuleRunnerBase(wasm,
externalInterface,
std::make_shared<EvallingImportResolver>(),
linkedInstances_) {}

Flow visitGlobalGet(GlobalGet* curr) {
// Error on reads of imported globals.
Expand Down Expand Up @@ -147,19 +166,6 @@ std::unique_ptr<Module> buildEnvModule(Module& wasm) {
}
});

ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) {
if (global->module == env->name) {
auto* copied = ModuleUtils::copyGlobal(global, *env);
copied->module = Name();
copied->base = Name();

Builder builder(*env);
copied->init = builder.makeConst(Literal::makeZero(global->type));
env->addExport(
builder.makeExport(global->base, copied->name, ExternalKind::Global));
}
});

// create an exported memory with the same initial and max size
ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) {
if (memory->module == env->name) {
Expand Down Expand Up @@ -231,26 +237,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
}
}

void importGlobals(GlobalValueSet& globals, Module& wasm_) override {
ModuleUtils::iterImportedGlobals(wasm_, [&](Global* global) {
auto it = linkedInstances.find(global->module);
if (it != linkedInstances.end()) {
auto* inst = it->second.get();
auto* globalExport = inst->wasm.getExportOrNull(global->base);
if (!globalExport || globalExport->kind != ExternalKind::Global) {
throw FailToEvalException(std::string("importGlobals: ") +
global->module.toString() + "." +
global->base.toString());
}
globals[global->name] = inst->globals[*globalExport->getInternalName()];
} else {
throw FailToEvalException(std::string("importGlobals: ") +
global->module.toString() + "." +
global->base.toString());
}
});
}

Literal getImportedFunction(Function* import) override {
auto f = [import, this](const Literals& arguments) -> Flow {
Name WASI("wasi_snapshot_preview1");
Expand Down Expand Up @@ -558,8 +544,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
void applyGlobalsToModule() {
if (!wasm->features.hasGC()) {
// Without GC, we can simply serialize the globals in place as they are.
for (const auto& [name, values] : instance->globals) {
wasm->getGlobal(name)->init = getSerialization(values);
for (const auto& [name, values] : instance->allGlobals) {
wasm->getGlobal(name)->init = getSerialization(*values);
}
return;
}
Expand All @@ -576,6 +562,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
wasm->updateMaps();

for (auto& oldGlobal : oldGlobals) {
if (oldGlobal->imported()) {
continue;
}
// Serialize the global's value. While doing so, pass in the name of this
// global, as we may be able to reuse the global as the defining global
// for the value. See getSerialization() for more details.
Expand All @@ -590,9 +579,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
// for it. (If there is no value, then this is a new global we've added
// during execution, for whom we've already set up a proper serialized
// value when we created it.)
auto iter = instance->globals.find(oldGlobal->name);
if (iter != instance->globals.end()) {
oldGlobal->init = getSerialization(iter->second, name);
auto iter = instance->allGlobals.find(oldGlobal->name);
if (iter != instance->allGlobals.end()) {
oldGlobal->init = getSerialization(*iter->second, name);
}

// Add the global back to the module.
Expand Down
2 changes: 1 addition & 1 deletion src/tools/wasm-shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ struct Shell {
}
auto& instance = it->second;
try {
return instance->getExportedGlobal(get->name);
return instance->getExportedGlobalOrTrap(get->name);
} catch (TrapException&) {
return TrapResult{};
} catch (...) {
Expand Down
104 changes: 67 additions & 37 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define wasm_wasm_interpreter_h

#include <cmath>
#include <iomanip>
#include <limits.h>
#include <sstream>
#include <variant>
Expand All @@ -46,6 +47,7 @@
#include "wasm-limits.h"
#include "wasm-traversal.h"
#include "wasm.h"
#include "wasm/import-resolver.h"

#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
#include <sanitizer/lsan_interface.h>
Expand Down Expand Up @@ -2940,8 +2942,6 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
}
};

using GlobalValueSet = std::map<Name, Literals>;

//
// A runner for a module. Each runner contains the information to execute the
// module, such as the state of globals, and so forth, so it basically
Expand Down Expand Up @@ -2969,7 +2969,6 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
std::map<Name, std::shared_ptr<SubType>> linkedInstances = {}) {}
virtual ~ExternalInterface() = default;
virtual void init(Module& wasm, SubType& instance) {}
virtual void importGlobals(GlobalValueSet& globals, Module& wasm) = 0;
virtual Literal getImportedFunction(Function* import) = 0;
virtual bool growMemory(Name name, Address oldSize, Address newSize) = 0;
virtual bool growTable(Name name,
Expand Down Expand Up @@ -3174,18 +3173,22 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
// TODO: this duplicates module in ExpressionRunner, and can be removed
Module& wasm;

// Values of globals
GlobalValueSet globals;

// Multivalue ABI support (see push/pop).
std::vector<Literals> multiValues;

// keyed by internal name
std::map<Name, Literals> definedGlobals;
std::map<Name, Literals*> allGlobals;

ModuleRunnerBase(
Module& wasm,
ExternalInterface* externalInterface,
std::shared_ptr<ImportResolver> importResolver,
std::map<Name, std::shared_ptr<SubType>> linkedInstances_ = {})
: ExpressionRunner<SubType>(&wasm), wasm(wasm),
externalInterface(externalInterface), linkedInstances(linkedInstances_) {
externalInterface(externalInterface),
linkedInstances(std::move(linkedInstances_)),
importResolver(std::move(importResolver)) {
// Set up a single shared CurrContinuations for all these linked instances,
// reusing one if it exists.
std::shared_ptr<ContinuationStore> shared;
Expand All @@ -3208,16 +3211,11 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
// (This is separate from the constructor so that it does not occur
// synchronously, which makes some code patterns harder to write.)
void instantiate() {
// import globals from the outside
externalInterface->importGlobals(globals, wasm);
// generate internal (non-imported) globals
ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) {
globals[global->name] = self()->visit(global->init).values;
});

// initialize the rest of the external interface
externalInterface->init(wasm, *self());

initializeGlobals();

initializeTableContents();
initializeMemoryContents();

Expand Down Expand Up @@ -3254,20 +3252,30 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
func->type);
}

// get an exported global
Literals getExportedGlobal(Name name) {
std::optional<Literals*> getExportedGlobal(Name name) {
Export* export_ = wasm.getExportOrNull(name);
if (!export_ || export_->kind != ExternalKind::Global) {
externalInterface->trap("getExport external not found");
return std::nullopt;
}
Name internalName = *export_->getInternalName();
auto iter = globals.find(internalName);
if (iter == globals.end()) {
externalInterface->trap("getExport internal not found");
auto iter = allGlobals.find(internalName);
if (iter == allGlobals.end()) {
return std::nullopt;
}
return iter->second;
}

Literals& getExportedGlobalOrTrap(Name name) {
auto global = getExportedGlobal(name);
if (!global.has_value()) {
externalInterface->trap((std::stringstream()
<< "getExportedGlobal: export " << name
<< " not found.")
.str());
}
return **global;
}

Tag* getExportedTag(Name name) {
Export* export_ = wasm.getExportOrNull(name);
if (!export_ || export_->kind != ExternalKind::Tag) {
Expand Down Expand Up @@ -3321,6 +3329,34 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
return TableInstanceInfo{self(), name};
}

void initializeGlobals() {
for (auto& global : wasm.globals) {
if (global->imported()) {
QualifiedName name{global->module, global->base};
auto importedGlobal = importResolver->getGlobal(name, global->type);
if (!importedGlobal) {
externalInterface->trap(
(std::stringstream() << "Imported global " << name << " not found.")
.str());
}
allGlobals[global->name] = *importedGlobal;
} else {
Literals init = self()->visit(global->init).values;
auto [it, inserted] = definedGlobals.emplace(global->name, init);

// This was likely checked during parsing or validation
if (!inserted) {
externalInterface->trap(
(std::stringstream()
<< "Global: " << std::quoted(global->name.toString())
<< " was defined twice.")
.str());
}
allGlobals[global->name] = &it->second;
}
}
}

void initializeTableContents() {
for (auto& table : wasm.tables) {
if (table->type.isNullable()) {
Expand Down Expand Up @@ -3511,20 +3547,8 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
SmallVector<std::pair<WasmException, Name>, 4> exceptionStack;

protected:
// Returns a reference to the current value of a potentially imported global.
Literals& getGlobal(Name name) {
auto* inst = self();
auto* global = inst->wasm.getGlobal(name);
while (global->imported()) {
inst = inst->linkedInstances.at(global->module).get();
Export* globalExport = inst->wasm.getExport(global->base);
global = inst->wasm.getGlobal(*globalExport->getInternalName());
}

return inst->globals[global->name];
}

// As above, but for a function.
// Returns a reference to the current value of a potentially imported
// function.
Literal getFunction(Name name) {
auto* inst = self();
auto* func = inst->wasm.getFunction(name);
Expand Down Expand Up @@ -3846,13 +3870,13 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {

Flow visitGlobalGet(GlobalGet* curr) {
auto name = curr->name;
return getGlobal(name);
return *allGlobals.at(name);
}
Flow visitGlobalSet(GlobalSet* curr) {
auto name = curr->name;
VISIT(flow, curr->value)

getGlobal(name) = flow.values;
*allGlobals.at(name) = flow.values;
return Flow();
}

Expand Down Expand Up @@ -5055,6 +5079,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {

ExternalInterface* externalInterface;
std::map<Name, std::shared_ptr<SubType>> linkedInstances;
std::shared_ptr<ImportResolver> importResolver;
};

class ModuleRunner : public ModuleRunnerBase<ModuleRunner> {
Expand All @@ -5063,7 +5088,12 @@ class ModuleRunner : public ModuleRunnerBase<ModuleRunner> {
Module& wasm,
ExternalInterface* externalInterface,
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances = {})
: ModuleRunnerBase(wasm, externalInterface, linkedInstances) {}
: ModuleRunnerBase(
wasm,
externalInterface,
std::make_shared<LinkedInstancesImportResolver<ModuleRunner>>(
linkedInstances),
linkedInstances) {}

Literal makeFuncData(Name name, Type type) {
// As the super's |makeFuncData|, but here we also provide a way to
Expand Down
Loading
Loading