Skip to content

Commit

Permalink
refactor: Allocate module dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Oct 5, 2020
1 parent 3ec1b4d commit fcee718
Show file tree
Hide file tree
Showing 21 changed files with 533 additions and 519 deletions.
20 changes: 10 additions & 10 deletions lib/fizzy/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,14 +467,14 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
if (depth > CallStackLimit)
return Trap;

const auto& func_type = instance.module.get_function_type(func_idx);
const auto& func_type = instance.module->get_function_type(func_idx);

assert(instance.module.imported_function_types.size() == instance.imported_functions.size());
assert(instance.module->imported_function_types.size() == instance.imported_functions.size());
if (func_idx < instance.imported_functions.size())
return instance.imported_functions[func_idx].function(
instance, {args, func_type.inputs.size()}, depth);

const auto& code = instance.module.get_code(func_idx);
const auto& code = instance.module->get_code(func_idx);
auto* const memory = instance.memory.get();

OperandStack stack(args, func_type.inputs.size(), code.local_count,
Expand Down Expand Up @@ -561,7 +561,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
case Instr::call:
{
const auto called_func_idx = read<uint32_t>(immediates);
const auto& called_func_type = instance.module.get_function_type(called_func_idx);
const auto& called_func_type = instance.module->get_function_type(called_func_idx);

if (!invoke_function(called_func_type, called_func_idx, instance, stack, depth))
goto trap;
Expand All @@ -572,7 +572,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
assert(instance.table != nullptr);

const auto expected_type_idx = read<uint32_t>(immediates);
assert(expected_type_idx < instance.module.typesec.size());
assert(expected_type_idx < instance.module->typesec.size());

const auto elem_idx = stack.pop().as<uint32_t>();
if (elem_idx >= instance.table->size())
Expand All @@ -584,7 +584,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,

// check actual type against expected type
const auto& actual_type = called_func->type;
const auto& expected_type = instance.module.typesec[expected_type_idx];
const auto& expected_type = instance.module->typesec[expected_type_idx];
if (expected_type != actual_type)
goto trap;

Expand Down Expand Up @@ -638,7 +638,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
else
{
const auto module_global_idx = idx - instance.imported_globals.size();
assert(module_global_idx < instance.module.globalsec.size());
assert(module_global_idx < instance.module->globalsec.size());
stack.push(instance.globals[module_global_idx]);
}
break;
Expand All @@ -654,8 +654,8 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
else
{
const auto module_global_idx = idx - instance.imported_globals.size();
assert(module_global_idx < instance.module.globalsec.size());
assert(instance.module.globalsec[module_global_idx].type.is_mutable);
assert(module_global_idx < instance.module->globalsec.size());
assert(instance.module->globalsec[module_global_idx].type.is_mutable);
instance.globals[module_global_idx] = stack.pop();
}
break;
Expand Down Expand Up @@ -1518,7 +1518,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,

end:
assert(pc == &code.instructions[code.instructions.size()]); // End of code must be reached.
assert(stack.size() == instance.module.get_function_type(func_idx).outputs.size());
assert(stack.size() == instance.module->get_function_type(func_idx).outputs.size());

return stack.size() != 0 ? ExecutionResult{stack.pop()} : Void;

Expand Down
2 changes: 1 addition & 1 deletion lib/fizzy/execute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ExecutionResult execute(
inline ExecutionResult execute(
Instance& instance, FuncIdx func_idx, std::initializer_list<Value> args) noexcept
{
assert(args.size() == instance.module.get_function_type(func_idx).inputs.size());
assert(args.size() == instance.module->get_function_type(func_idx).inputs.size());
return execute(instance, func_idx, args.begin());
}
} // namespace fizzy
78 changes: 42 additions & 36 deletions lib/fizzy/instantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,22 +237,22 @@ std::optional<uint32_t> find_export(const Module& module, ExternalKind kind, std

} // namespace

std::unique_ptr<Instance> instantiate(Module module,
std::unique_ptr<Instance> instantiate(std::unique_ptr<Module>&& module,
std::vector<ExternalFunction> imported_functions, std::vector<ExternalTable> imported_tables,
std::vector<ExternalMemory> imported_memories, std::vector<ExternalGlobal> imported_globals,
uint32_t memory_pages_limit /*= DefaultMemoryPagesLimit*/)
{
assert(module.funcsec.size() == module.codesec.size());
assert(module->funcsec.size() == module->codesec.size());

match_imported_functions(module.imported_function_types, imported_functions);
match_imported_tables(module.imported_table_types, imported_tables);
match_imported_memories(module.imported_memory_types, imported_memories);
match_imported_globals(module.imported_global_types, imported_globals);
match_imported_functions(module->imported_function_types, imported_functions);
match_imported_tables(module->imported_table_types, imported_tables);
match_imported_memories(module->imported_memory_types, imported_memories);
match_imported_globals(module->imported_global_types, imported_globals);

// Init globals
std::vector<Value> globals;
globals.reserve(module.globalsec.size());
for (auto const& global : module.globalsec)
globals.reserve(module->globalsec.size());
for (auto const& global : module->globalsec)
{
// Constraint to use global.get only with imported globals is checked at validation.
assert(global.expression.kind != ConstantExpression::Kind::GlobalGet ||
Expand All @@ -262,10 +262,10 @@ std::unique_ptr<Instance> instantiate(Module module,
globals.emplace_back(value);
}

auto [table, table_limits] = allocate_table(module.tablesec, imported_tables);
auto [table, table_limits] = allocate_table(module->tablesec, imported_tables);

auto [memory, memory_limits] =
allocate_memory(module.memorysec, imported_memories, memory_pages_limit);
allocate_memory(module->memorysec, imported_memories, memory_pages_limit);
// In case upper limit for local/imported memory is defined,
// we adjust the hard memory limit, to ensure memory.grow will fail when exceeding it.
// Note: allocate_memory ensures memory's max limit is always below memory_pages_limit.
Expand All @@ -278,8 +278,8 @@ std::unique_ptr<Instance> instantiate(Module module,
// Before starting to fill memory and table,
// check that data and element segments are within bounds.
std::vector<uint64_t> datasec_offsets;
datasec_offsets.reserve(module.datasec.size());
for (const auto& data : module.datasec)
datasec_offsets.reserve(module->datasec.size());
for (const auto& data : module->datasec)
{
// Offset is validated to be i32, but it's used in 64-bit calculation below.
const uint64_t offset =
Expand All @@ -291,10 +291,10 @@ std::unique_ptr<Instance> instantiate(Module module,
datasec_offsets.emplace_back(offset);
}

assert(module.elementsec.empty() || table != nullptr);
assert(module->elementsec.empty() || table != nullptr);
std::vector<ptrdiff_t> elementsec_offsets;
elementsec_offsets.reserve(module.elementsec.size());
for (const auto& element : module.elementsec)
elementsec_offsets.reserve(module->elementsec.size());
for (const auto& element : module->elementsec)
{
// Offset is validated to be i32, but it's used in 64-bit calculation below.
const uint64_t offset =
Expand All @@ -307,10 +307,10 @@ std::unique_ptr<Instance> instantiate(Module module,
}

// Fill out memory based on data segments
for (size_t i = 0; i < module.datasec.size(); ++i)
for (size_t i = 0; i < module->datasec.size(); ++i)
{
// NOTE: these instructions can overlap
std::copy(module.datasec[i].init.begin(), module.datasec[i].init.end(),
std::copy(module->datasec[i].init.begin(), module->datasec[i].init.end(),
memory->data() + datasec_offsets[i]);
}

Expand All @@ -321,41 +321,41 @@ std::unique_ptr<Instance> instantiate(Module module,
std::move(imported_functions), std::move(imported_globals));

// Fill the table based on elements segment
for (size_t i = 0; i < instance->module.elementsec.size(); ++i)
for (size_t i = 0; i < instance->module->elementsec.size(); ++i)
{
// Overwrite table[offset..] with element.init
auto it_table = instance->table->begin() + elementsec_offsets[i];
for (const auto idx : instance->module.elementsec[i].init)
for (const auto idx : instance->module->elementsec[i].init)
{
auto func = [idx, &instance_ref = *instance](fizzy::Instance&, span<const Value> args,
int depth) { return execute(instance_ref, idx, args.data(), depth); };

*it_table++ =
ExternalFunction{std::move(func), instance->module.get_function_type(idx)};
ExternalFunction{std::move(func), instance->module->get_function_type(idx)};
}
}

// Run start function if present
if (instance->module.startfunc)
if (instance->module->startfunc)
{
const auto funcidx = *instance->module.startfunc;
assert(funcidx < instance->imported_functions.size() + instance->module.funcsec.size());
const auto funcidx = *instance->module->startfunc;
assert(funcidx < instance->imported_functions.size() + instance->module->funcsec.size());
if (execute(*instance, funcidx, {}).trapped)
{
// When element section modified imported table, and then start function trapped,
// modifications to the table are not rolled back.
// Instance in this case is not being returned to the user, so it needs to be kept alive
// as long as functions using it are alive in the table.
if (!imported_tables.empty() && !instance->module.elementsec.empty())
if (!imported_tables.empty() && !instance->module->elementsec.empty())
{
// Instance may be used by several functions added to the table,
// so we need a shared ownership here.
std::shared_ptr<Instance> shared_instance = std::move(instance);

for (size_t i = 0; i < shared_instance->module.elementsec.size(); ++i)
for (size_t i = 0; i < shared_instance->module->elementsec.size(); ++i)
{
auto it_table = shared_instance->table->begin() + elementsec_offsets[i];
for ([[maybe_unused]] auto _ : shared_instance->module.elementsec[i].init)
for ([[maybe_unused]] auto _ : shared_instance->module->elementsec[i].init)
{
// Wrap the function with the lambda capturing shared instance
auto& table_function = (*it_table)->function;
Expand All @@ -373,6 +373,16 @@ std::unique_ptr<Instance> instantiate(Module module,
return instance;
}

std::unique_ptr<Instance> instantiate(const std::unique_ptr<Module>& module,
std::vector<ExternalFunction> imported_functions, std::vector<ExternalTable> imported_tables,
std::vector<ExternalMemory> imported_memories, std::vector<ExternalGlobal> imported_globals,
uint32_t memory_pages_limit)
{
return instantiate(std::make_unique<Module>(*module), std::move(imported_functions),
std::move(imported_tables), std::move(imported_memories), std::move(imported_globals),
memory_pages_limit);
}

std::vector<ExternalFunction> resolve_imported_functions(
const Module& module, std::vector<ImportedFunction> imported_functions)
{
Expand Down Expand Up @@ -427,7 +437,7 @@ std::optional<FuncIdx> find_exported_function(const Module& module, std::string_

std::optional<ExternalFunction> find_exported_function(Instance& instance, std::string_view name)
{
const auto opt_index = find_export(instance.module, ExternalKind::Function, name);
const auto opt_index = find_export(*instance.module, ExternalKind::Function, name);
if (!opt_index.has_value())
return std::nullopt;

Expand All @@ -436,12 +446,12 @@ std::optional<ExternalFunction> find_exported_function(Instance& instance, std::
return execute(instance, idx, args.data(), depth);
};

return ExternalFunction{std::move(func), instance.module.get_function_type(idx)};
return ExternalFunction{std::move(func), instance.module->get_function_type(idx)};
}

std::optional<ExternalGlobal> find_exported_global(Instance& instance, std::string_view name)
{
const auto opt_index = find_export(instance.module, ExternalKind::Global, name);
const auto opt_index = find_export(*instance.module, ExternalKind::Global, name);
if (!opt_index.has_value())
return std::nullopt;

Expand All @@ -457,27 +467,23 @@ std::optional<ExternalGlobal> find_exported_global(Instance& instance, std::stri
// global owned by instance
const auto module_global_idx = global_idx - instance.imported_globals.size();
return ExternalGlobal{&instance.globals[module_global_idx],
instance.module.globalsec[module_global_idx].type};
instance.module->globalsec[module_global_idx].type};
}
}

std::optional<ExternalTable> find_exported_table(Instance& instance, std::string_view name)
{
const auto& module = instance.module;

// Index returned from find_export is discarded, because there's no more than 1 table
if (!find_export(module, ExternalKind::Table, name))
if (!find_export(*instance.module, ExternalKind::Table, name))
return std::nullopt;

return ExternalTable{instance.table.get(), instance.table_limits};
}

std::optional<ExternalMemory> find_exported_memory(Instance& instance, std::string_view name)
{
const auto& module = instance.module;

// Index returned from find_export is discarded, because there's no more than 1 memory
if (!find_export(module, ExternalKind::Memory, name))
if (!find_export(*instance.module, ExternalKind::Memory, name))
return std::nullopt;

return ExternalMemory{instance.memory.get(), instance.memory_limits};
Expand Down
16 changes: 11 additions & 5 deletions lib/fizzy/instantiate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ using bytes_ptr = std::unique_ptr<bytes, void (*)(bytes*)>;
// The module instance.
struct Instance
{
Module module;
std::unique_ptr<Module> module;
// Memory is either allocated and owned by the instance or imported as already allocated bytes
// and owned externally.
// For these cases unique_ptr would either have a normal deleter or noop deleter respectively
Expand All @@ -70,9 +70,9 @@ struct Instance
std::vector<ExternalFunction> imported_functions;
std::vector<ExternalGlobal> imported_globals;

Instance(Module _module, bytes_ptr _memory, Limits _memory_limits, uint32_t _memory_pages_limit,
table_ptr _table, Limits _table_limits, std::vector<Value> _globals,
std::vector<ExternalFunction> _imported_functions,
Instance(std::unique_ptr<Module> _module, bytes_ptr _memory, Limits _memory_limits,
uint32_t _memory_pages_limit, table_ptr _table, Limits _table_limits,
std::vector<Value> _globals, std::vector<ExternalFunction> _imported_functions,
std::vector<ExternalGlobal> _imported_globals)
: module(std::move(_module)),
memory(std::move(_memory)),
Expand All @@ -87,13 +87,19 @@ struct Instance
};

// Instantiate a module.
std::unique_ptr<Instance> instantiate(Module module,
std::unique_ptr<Instance> instantiate(std::unique_ptr<Module>&& module,
std::vector<ExternalFunction> imported_functions = {},
std::vector<ExternalTable> imported_tables = {},
std::vector<ExternalMemory> imported_memories = {},
std::vector<ExternalGlobal> imported_globals = {},
uint32_t memory_pages_limit = DefaultMemoryPagesLimit);

std::unique_ptr<Instance> instantiate(const std::unique_ptr<Module>& module,
std::vector<ExternalFunction> imported_functions = {},
std::vector<ExternalTable> imported_tables = {},
std::vector<ExternalMemory> imported_memories = {},
std::vector<ExternalGlobal> imported_globals = {},
uint32_t memory_pages_limit = DefaultMemoryPagesLimit);

// Function that should be used by instantiate as imports, identified by module and function name.
struct ImportedFunction
Expand Down
Loading

0 comments on commit fcee718

Please sign in to comment.