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
1 change: 1 addition & 0 deletions src/ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(ir_SOURCES
public-type-validator.cpp
ReFinalize.cpp
return-utils.cpp
runtime-table.cpp
stack-utils.cpp
table-utils.cpp
type-updating.cpp
Expand Down
16 changes: 16 additions & 0 deletions src/ir/import-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define wasm_ir_import_h

#include "ir/import-name.h"
#include "ir/runtime-table.h"
#include "literal.h"
#include "wasm.h"

Expand Down Expand Up @@ -131,6 +132,10 @@ class ImportResolver {
// Returns null if the `name` wasn't found. The returned Literals* lives as
// long as the ImportResolver instance.
virtual Literals* getGlobalOrNull(ImportNames name, Type type) const = 0;

// TODO: new type for TableType or just re-use the existing one?
virtual RuntimeTable* getTableOrNull(ImportNames name,
const Table& type) const = 0;
};

// Looks up imports from the given `linkedInstances`.
Expand All @@ -151,6 +156,17 @@ class LinkedInstancesImportResolver : public ImportResolver {
return instance->getExportedGlobalOrNull(name.name);
}

RuntimeTable* getTableOrNull(ImportNames name,
const Table& type) const override {
auto it = linkedInstances.find(name.module);
if (it == linkedInstances.end()) {
return nullptr;
}

ModuleRunnerType* instance = it->second.get();
return instance->getExportedTableOrNull(name.name);
}

private:
const std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances;
};
Expand Down
41 changes: 41 additions & 0 deletions src/ir/runtime-table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "ir/runtime-table.h"
#include "support/stdckdint.h"
#include "wasm-limits.h"

namespace wasm {

void RuntimeTable::set(std::size_t i, Literal l) {
if (i >= table.size()) {
trap("RuntimeTable::set out of bounds");
WASM_UNREACHABLE("trapped");
}

table[i] = std::move(l);
}

Literal RuntimeTable::get(std::size_t i) const {
if (i >= table.size()) {
trap("out of bounds table access");
WASM_UNREACHABLE("trapped");
}

return table[i];
}

std::optional<std::size_t> RuntimeTable::grow(std::size_t delta, Literal fill) {
std::size_t newSize;
if (std::ckd_add(&newSize, table.size(), delta)) {
return std::nullopt;
}

// TODO: this size is different than in wasm-interpreter.h
if (newSize > WebLimitations::MaxTableSize || newSize > tableMeta_.max) {
return std::nullopt;
}

std::size_t oldSize = table.size();
table.resize(newSize, fill);
return oldSize;
}

} // namespace wasm
39 changes: 39 additions & 0 deletions src/ir/runtime-table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef wasm_ir_runtime_table_h
#define wasm_ir_runtime_table_h

#include <stddef.h>
#include <vector>

#include "literal.h"
#include "wasm.h"

namespace wasm {

// Traps on out of bounds access
class RuntimeTable {
public:
// TODO: constructor for initial contents?
RuntimeTable(Table table_, std::function<void(std::string_view s)> trap_)
: tableMeta_(table_), trap(trap_) {}

void set(std::size_t i, Literal l);

Literal get(std::size_t i) const;

// Returns nullopt if the table grew beyond the max possible size.
[[nodiscard]] std::optional<std::size_t> grow(std::size_t delta,
Literal fill);

std::size_t size() const { return table.size(); }

const Table* tableMeta() const { return &tableMeta_; }

private:
const Table tableMeta_;
std::vector<Literal> table;
std::function<void(std::string_view)> trap;
};

} // namespace wasm

#endif // wasm_ir_runtime_table_h
46 changes: 0 additions & 46 deletions src/shell-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
};

std::map<Name, Memory> memories;
std::unordered_map<Name, std::vector<Literal>> tables;
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;

ShellExternalInterface(
Expand Down Expand Up @@ -125,8 +124,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
shellMemory.resize(memory->initial * wasm::Memory::kPageSize);
memories[memory->name] = shellMemory;
});
ModuleUtils::iterDefinedTables(
wasm, [&](Table* table) { tables[table->name].resize(table->initial); });
}

Literal getImportedFunction(Function* import) override {
Expand Down Expand Up @@ -255,35 +252,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
auto& memory = it->second;
memory.set<std::array<uint8_t, 16>>(addr, value);
}

Index tableSize(Name tableName) override {
return (Index)tables[tableName].size();
}

void
tableStore(Name tableName, Address index, const Literal& entry) override {
auto& table = tables[tableName];
if (index >= table.size()) {
trap("out of bounds table access");
} else {
table[index] = entry;
}
}

Literal tableLoad(Name tableName, Address index) override {
auto it = tables.find(tableName);
if (it == tables.end()) {
trap("tableGet on non-existing table");
}

auto& table = it->second;
if (index >= table.size()) {
trap("out of bounds table access");
}

return table[index];
}

bool
growMemory(Name memoryName, Address /*oldSize*/, Address newSize) override {
// Apply a reasonable limit on memory size, 1GB, to avoid DOS on the
Expand All @@ -299,20 +267,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
memory.resize(newSize);
return true;
}

bool growTable(Name name,
const Literal& value,
Index /*oldSize*/,
Index newSize) override {
// Apply a reasonable limit on table size, 1GB, to avoid DOS on the
// interpreter.
if (newSize > 1024 * 1024 * 1024) {
return false;
}
tables[name].resize(newSize, value);
return true;
}

void trap(std::string_view why) override {
std::cout << "[trap " << why << "]\n";
throw TrapException();
Expand Down
10 changes: 6 additions & 4 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,21 @@ struct LoggingExternalInterface : public ShellExternalInterface {
throwJSException();
}
auto index = arguments[0].getUnsigned();
if (index >= tables[exportedTable].size()) {
auto* table = instance->allTables[exportedTable];
if (index >= table->size()) {
throwJSException();
}
return {tableLoad(exportedTable, index)};
return table->get(index);
} else if (import->base == "table-set") {
if (!exportedTable) {
throwJSException();
}
auto index = arguments[0].getUnsigned();
if (index >= tables[exportedTable].size()) {
auto* table = instance->allTables[exportedTable];
if (index >= table->size()) {
throwJSException();
}
tableStore(exportedTable, index, arguments[1]);
table->set(index, arguments[1]);
return {};
} else if (import->base == "call-export") {
callExportAsJS(arguments[0].geti32());
Expand Down
91 changes: 9 additions & 82 deletions src/tools/wasm-ctor-eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "ir/memory-utils.h"
#include "ir/names.h"
#include "pass.h"
#include "shell-interface.h"
#include "support/colors.h"
#include "support/file.h"
#include "support/insert_ordered.h"
Expand Down Expand Up @@ -80,6 +81,11 @@ class EvallingImportResolver : public ImportResolver {
return &stubLiteral;
}

RuntimeTable* getTableOrNull(ImportNames name,
const Table& type) const override {
throw FailToEvalException{"Imported table access."};
}

private:
mutable Literals stubLiteral;
};
Expand Down Expand Up @@ -156,17 +162,6 @@ std::unique_ptr<Module> buildEnvModule(Module& wasm) {
}
});

// create tables with similar initial and max values
ModuleUtils::iterImportedTables(wasm, [&](Table* table) {
if (table->module == env->name) {
auto* copied = ModuleUtils::copyTable(table, *env);
copied->module = Name();
copied->base = Name();
env->addExport(Builder(*env).makeExport(
table->base, copied->name, ExternalKind::Table));
}
});

// 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 @@ -315,70 +310,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
WASM_UNREACHABLE("missing imported tag");
}

// We assume the table is not modified FIXME
Literal tableLoad(Name tableName, Address index) override {
auto* table = wasm->getTableOrNull(tableName);
if (!table) {
throw FailToEvalException("tableLoad on non-existing table");
}

// Look through the segments and find the value. Segments can overlap,
// so we want the last one.
Expression* value = nullptr;
for (auto& segment : wasm->elementSegments) {
if (segment->table != tableName) {
continue;
}

Index start;
// look for the index in this segment. if it has a constant offset, we
// look in the proper range. if it instead gets a global, we rely on the
// fact that when not dynamically linking then the table is loaded at
// offset 0.
if (auto* c = segment->offset->dynCast<Const>()) {
start = c->value.getInteger();
} else if (segment->offset->is<GlobalGet>()) {
start = 0;
} else {
// wasm spec only allows const and global.get there
WASM_UNREACHABLE("invalid expr type");
}
auto end = start + segment->data.size();
if (start <= index && index < end) {
value = segment->data[index - start];
}
}

if (!value) {
// No segment had a value for this.
return Literal::makeNull(HeapTypes::func);
}
if (!Properties::isConstantExpression(value)) {
throw FailToEvalException("tableLoad of non-literal");
}
if (auto* r = value->dynCast<RefFunc>()) {
return instance->makeFuncData(r->func, r->type);
}
return Properties::getLiteral(value);
}

Index tableSize(Name tableName) override {
// See tableLoad above, we assume the table is not modified FIXME
return wasm->getTableOrNull(tableName)->initial;
}

// called during initialization
void
tableStore(Name tableName, Address index, const Literal& value) override {
// We allow stores to the table during initialization, but not after, as we
// assume the table does not change at runtime.
// TODO: Allow table changes by updating the table later like we do with the
// memory, by tracking and serializing them.
if (instanceInitialized) {
throw FailToEvalException("tableStore after init: TODO");
}
}

int8_t load8s(Address addr, Name memoryName) override {
return doLoad<int8_t>(addr, memoryName);
}
Expand Down Expand Up @@ -431,13 +362,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
throw FailToEvalException("grow memory");
}

bool growTable(Name /*name*/,
const Literal& /*value*/,
Index /*oldSize*/,
Index /*newSize*/) override {
throw FailToEvalException("grow table");
}

void trap(std::string_view why) override {
throw FailToEvalException(std::string("trap: ") + std::string(why));
}
Expand Down Expand Up @@ -1145,6 +1069,9 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
std::cout << " ...stopping due to non-constant func\n";
}
break;
} catch (TrapException trap) {
std::cout << " ...stopping due to trap";
break;
}

if (flow.breakTo == NONCONSTANT_FLOW) {
Expand Down
Loading
Loading