Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

capi: Remove dynamic allocation in find_exported_function #791

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
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
11 changes: 2 additions & 9 deletions include/fizzy/fizzy.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ typedef struct FizzyExternalFunction
{
/// Function type.
FizzyFunctionType type;
FizzyInstance* instance;
uint32_t function_index;
/// Pointer to function.
FizzyExternalFn function;
/// Opaque pointer to execution context, that will be passed to function.
Expand Down Expand Up @@ -556,15 +558,6 @@ size_t fizzy_get_instance_memory_size(FizzyInstance* instance) FIZZY_NOEXCEPT;
bool fizzy_find_exported_function(
FizzyInstance* instance, const char* name, FizzyExternalFunction* out_function) FIZZY_NOEXCEPT;

/// Free resources associated with exported function.
///
/// @param external_function Pointer to external function struct filled by
/// fizzy_find_exported_function(). Cannot be NULL.
///
/// @note This function may not be called with external function, which was not returned from
/// fizzy_find_exported_function().
void fizzy_free_exported_function(FizzyExternalFunction* external_function) FIZZY_NOEXCEPT;

/// Find exported table by name.
///
/// @param instance Pointer to instance. Cannot be NULL.
Expand Down
111 changes: 39 additions & 72 deletions lib/fizzy/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,21 +133,11 @@ inline FizzyValue wrap(fizzy::Value value) noexcept
return fizzy::bit_cast<FizzyValue>(value);
}

inline fizzy::Value unwrap(FizzyValue value) noexcept
{
return fizzy::bit_cast<fizzy::Value>(value);
}

inline FizzyValue* wrap(fizzy::Value* values) noexcept
{
return reinterpret_cast<FizzyValue*>(values);
}

inline const FizzyValue* wrap(const fizzy::Value* values) noexcept
{
return reinterpret_cast<const FizzyValue*>(values);
}

inline const fizzy::Value* unwrap(const FizzyValue* values) noexcept
{
return reinterpret_cast<const fizzy::Value*>(values);
Expand All @@ -158,16 +148,6 @@ inline fizzy::Value* unwrap(FizzyValue* value) noexcept
return reinterpret_cast<fizzy::Value*>(value);
}

inline FizzyExecutionContext* wrap(fizzy::ExecutionContext& ctx) noexcept
{
return reinterpret_cast<FizzyExecutionContext*>(&ctx);
}

inline fizzy::ExecutionContext& unwrap(FizzyExecutionContext* ctx) noexcept
{
return *reinterpret_cast<fizzy::ExecutionContext*>(ctx);
}

inline FizzyInstance* wrap(fizzy::Instance* instance) noexcept
{
return reinterpret_cast<FizzyInstance*>(instance);
Expand All @@ -183,49 +163,20 @@ inline FizzyExecutionResult wrap(const fizzy::ExecutionResult& result) noexcept
return {result.trapped, result.has_value, wrap(result.value)};
}

inline fizzy::ExecutionResult unwrap(const FizzyExecutionResult& result) noexcept
{
if (result.trapped)
return fizzy::Trap;
else if (!result.has_value)
return fizzy::Void;
else
return unwrap(result.value);
}

inline fizzy::ExecuteFunction unwrap(FizzyExternalFn c_function, void* c_host_context)
{
static constexpr fizzy::HostFunctionPtr function =
[](std::any& host_ctx, fizzy::Instance& instance, const fizzy::Value* args,
fizzy::ExecutionContext& ctx) noexcept {
const auto [c_func, c_host_ctx] =
*std::any_cast<std::pair<FizzyExternalFn, void*>>(&host_ctx);
return unwrap(c_func(c_host_ctx, wrap(&instance), wrap(args), wrap(ctx)));
};

return {function, std::make_any<std::pair<FizzyExternalFn, void*>>(c_function, c_host_context)};
}

inline FizzyExternalFunction wrap(fizzy::ExternalFunction external_func)
{
static constexpr FizzyExternalFn c_function =
[](void* host_ctx, FizzyInstance* instance, const FizzyValue* args,
FizzyExecutionContext* c_ctx) noexcept -> FizzyExecutionResult {
// If execution context not provided, allocate new one.
// It must be allocated on heap otherwise the stack will explode in recursive calls.
std::unique_ptr<fizzy::ExecutionContext> new_ctx;
if (c_ctx == nullptr)
new_ctx = std::make_unique<fizzy::ExecutionContext>();
auto& ctx = new_ctx ? *new_ctx : unwrap(c_ctx);

auto* func = static_cast<fizzy::ExternalFunction*>(host_ctx);
return wrap((func->function)(*unwrap(instance), unwrap(args), ctx));
};
const auto c_type = wrap(external_func.input_types, external_func.output_types);

auto host_ctx = std::make_unique<fizzy::ExternalFunction>(std::move(external_func));
const auto c_type = wrap(host_ctx->input_types, host_ctx->output_types);
void* c_host_ctx = host_ctx.release();
return {c_type, c_function, c_host_ctx};
if (external_func.function.get_c_host_function())
{
return {c_type, nullptr, 0, external_func.function.get_c_host_function(),
std::any_cast<void*&>(external_func.function.get_host_context())};
}
else
{
return {c_type, wrap(external_func.function.get_instance()),
external_func.function.get_function_index(), nullptr, nullptr};
}
}

inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func)
Expand All @@ -237,8 +188,16 @@ inline fizzy::ExternalFunction unwrap(const FizzyExternalFunction& external_func
fizzy::span<const fizzy::ValType>{} :
fizzy::span<const fizzy::ValType>{unwrap(&external_func.type.output), 1});

return fizzy::ExternalFunction{
unwrap(external_func.function, external_func.context), input_types, output_types};
if (external_func.instance != nullptr)
{
fizzy::ExecuteFunction func(*unwrap(external_func.instance), external_func.function_index);
return {std::move(func), input_types, output_types};
}
else
{
fizzy::ExecuteFunction func(external_func.function, external_func.context);
return {std::move(func), input_types, output_types};
}
}

inline std::vector<fizzy::ExternalFunction> unwrap(
Expand All @@ -265,11 +224,20 @@ inline fizzy::ImportedFunction unwrap(const FizzyImportedFunction& c_imported_fu
std::nullopt :
std::make_optional(unwrap(c_type.output)));

auto function = unwrap(
c_imported_func.external_function.function, c_imported_func.external_function.context);

return {c_imported_func.module, c_imported_func.name, std::move(inputs), output,
std::move(function)};
if (c_imported_func.external_function.instance != nullptr)
{
fizzy::ExecuteFunction func(*unwrap(c_imported_func.external_function.instance),
c_imported_func.external_function.function_index);
return {c_imported_func.module, c_imported_func.name, std::move(inputs), output,
std::move(func)};
}
else
{
fizzy::ExecuteFunction func(
c_imported_func.external_function.function, c_imported_func.external_function.context);
return {c_imported_func.module, c_imported_func.name, std::move(inputs), output,
std::move(func)};
}
}

inline std::vector<fizzy::ImportedFunction> unwrap(
Expand Down Expand Up @@ -559,6 +527,10 @@ bool fizzy_find_exported_function(
if (!optional_func)
return false;

// C++ host functions not supported
if (optional_func->function.get_host_function() != nullptr)
return false;

try
{
*out_function = wrap(std::move(*optional_func));
Expand All @@ -570,11 +542,6 @@ bool fizzy_find_exported_function(
}
}

void fizzy_free_exported_function(FizzyExternalFunction* external_function) noexcept
{
delete static_cast<fizzy::ExternalFunction*>(external_function->context);
}

bool fizzy_find_exported_table(
FizzyInstance* instance, const char* name, FizzyExternalTable* out_table) noexcept
{
Expand Down
16 changes: 15 additions & 1 deletion lib/fizzy/instantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

#include "instantiate.hpp"
#include "cxx20/bit.hpp"
#include "execute.hpp" // needed for implementation of ExecuteFunction for Wasm functions
#include <algorithm>
#include <cassert>
Expand Down Expand Up @@ -322,8 +323,21 @@ ExecutionResult ExecuteFunction::operator()(
{
if (m_instance)
return execute(*m_instance, m_func_idx, args, ctx);
else
else if (m_host_function)
return m_host_function(m_host_context, instance, args, ctx);
else
{
assert(m_c_host_function != nullptr);
const auto result = m_c_host_function(std::any_cast<void*&>(m_host_context),
reinterpret_cast<FizzyInstance*>(&instance), reinterpret_cast<const FizzyValue*>(args),
reinterpret_cast<FizzyExecutionContext*>(&ctx));
if (result.trapped)
return fizzy::Trap;
else if (!result.has_value)
return fizzy::Void;
else
return fizzy::bit_cast<fizzy::Value>(result.value);
}
}

std::unique_ptr<Instance> instantiate(std::unique_ptr<const Module> module,
Expand Down
15 changes: 15 additions & 0 deletions lib/fizzy/instantiate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "module.hpp"
#include "types.hpp"
#include "value.hpp"
#include <fizzy/fizzy.h>
#include <any>
#include <cstdint>
#include <functional>
Expand Down Expand Up @@ -42,6 +43,8 @@ class ExecuteFunction
/// Equals nullptr in case this ExecuteFunction represents WebAssembly function.
HostFunctionPtr m_host_function = nullptr;

FizzyExternalFn m_c_host_function = nullptr;

/// Opaque context of host function execution, which is passed to it as host_context parameter.
/// Doesn't have value in case this ExecuteFunction represents WebAssembly function.
std::any m_host_context;
Expand All @@ -67,8 +70,20 @@ class ExecuteFunction
ExecutionResult operator()(
Instance& instance, const Value* args, ExecutionContext& ctx) noexcept;

ExecuteFunction(FizzyExternalFn f, void* host_context) noexcept
: m_c_host_function{f}, m_host_context{host_context}
{}

Instance* get_instance() const noexcept { return m_instance; }

/// Function pointer stored inside this object.
HostFunctionPtr get_host_function() const noexcept { return m_host_function; }

FizzyExternalFn get_c_host_function() const noexcept { return m_c_host_function; }

FuncIdx get_function_index() const noexcept { return m_func_idx; }

std::any& get_host_context() noexcept { return m_host_context; }
};

/// Function with associated input/output types,
Expand Down
44 changes: 26 additions & 18 deletions test/unittests/capi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,12 +479,14 @@ TEST(capi, find_exported_function)
ASSERT_TRUE(fizzy_find_exported_function(instance, "foo", &function));
EXPECT_EQ(function.type.inputs_size, 0);
EXPECT_EQ(function.type.output, FizzyValueTypeI32);
EXPECT_NE(function.context, nullptr);
ASSERT_NE(function.function, nullptr);
EXPECT_EQ(function.context, nullptr);
ASSERT_EQ(function.function, nullptr);
EXPECT_EQ(function.instance, instance);
EXPECT_EQ(function.function_index, 0);

EXPECT_THAT(function.function(function.context, instance, nullptr, nullptr), CResult(42_u32));

fizzy_free_exported_function(&function);
// EXPECT_THAT(function.function(function.context, instance, nullptr, nullptr),
// CResult(42_u32));

EXPECT_FALSE(fizzy_find_exported_function(instance, "foo2", &function));
EXPECT_FALSE(fizzy_find_exported_function(instance, "g1", &function));
Expand Down Expand Up @@ -731,7 +733,8 @@ TEST(capi, instantiate_imported_function)
module = fizzy_parse(wasm.data(), wasm.size(), nullptr);
ASSERT_NE(module, nullptr);

FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0}, NullFn, nullptr}};
FizzyExternalFunction host_funcs[] = {
{{FizzyValueTypeI32, nullptr, 0}, nullptr, 0, NullFn, nullptr}};

auto instance = fizzy_instantiate(
module, host_funcs, 1, nullptr, nullptr, nullptr, 0, FizzyMemoryPagesLimitDefault, nullptr);
Expand Down Expand Up @@ -921,7 +924,7 @@ TEST(capi, resolve_instantiate_no_imports)

// providing unnecessary import
FizzyImportedFunction host_funcs[] = {
{"mod", "foo", {{FizzyValueTypeVoid, nullptr, 0}, NullFn, nullptr}}};
{"mod", "foo", {{FizzyValueTypeVoid, nullptr, 0}, nullptr, 0, NullFn, nullptr}}};

instance = fizzy_resolve_instantiate(
module, host_funcs, 1, nullptr, nullptr, nullptr, 0, FizzyMemoryPagesLimitDefault, nullptr);
Expand Down Expand Up @@ -976,15 +979,19 @@ TEST(capi, resolve_instantiate_functions)

const FizzyValueType input_type = FizzyValueTypeI32;
FizzyValue result_int32{42};
FizzyExternalFunction mod1foo1 = {{FizzyValueTypeI32, &input_type, 1}, host_fn, &result_int32};
FizzyExternalFunction mod1foo1 = {
{FizzyValueTypeI32, &input_type, 1}, nullptr, 0, host_fn, &result_int32};
FizzyValue result_int64{43};
FizzyExternalFunction mod1foo2 = {{FizzyValueTypeI64, &input_type, 1}, host_fn, &result_int64};
FizzyExternalFunction mod1foo2 = {
{FizzyValueTypeI64, &input_type, 1}, nullptr, 0, host_fn, &result_int64};
FizzyValue result_f32;
result_f32.f32 = 44.44f;
FizzyExternalFunction mod2foo1 = {{FizzyValueTypeF32, &input_type, 1}, host_fn, &result_f32};
FizzyExternalFunction mod2foo1 = {
{FizzyValueTypeF32, &input_type, 1}, nullptr, 0, host_fn, &result_f32};
FizzyValue result_f64;
result_f64.f64 = 45.45;
FizzyExternalFunction mod2foo2 = {{FizzyValueTypeF64, &input_type, 1}, host_fn, &result_f64};
FizzyExternalFunction mod2foo2 = {
{FizzyValueTypeF64, &input_type, 1}, nullptr, 0, host_fn, &result_f64};

FizzyImportedFunction host_funcs[] = {{"mod1", "foo1", mod1foo1}, {"mod1", "foo2", mod1foo2},
{"mod2", "foo1", mod2foo1}, {"mod2", "foo2", mod2foo2}};
Expand Down Expand Up @@ -1048,7 +1055,8 @@ TEST(capi, resolve_instantiate_function_duplicate)
return FizzyExecutionResult{false, true, FizzyValue{42}};
};

FizzyExternalFunction mod1foo1 = {{FizzyValueTypeI32, nullptr, 0}, host_fn, nullptr};
FizzyExternalFunction mod1foo1 = {
{FizzyValueTypeI32, nullptr, 0}, nullptr, 0, host_fn, nullptr};
FizzyImportedFunction host_funcs[] = {{"mod1", "foo1", mod1foo1}};

auto instance = fizzy_resolve_instantiate(
Expand Down Expand Up @@ -1096,7 +1104,7 @@ TEST(capi, resolve_instantiate_globals)
return FizzyExecutionResult{true, false, {0}};
};
FizzyImportedFunction mod1foo1 = {
"mod1", "foo1", {{FizzyValueTypeVoid, nullptr, 0}, host_fn, nullptr}};
"mod1", "foo1", {{FizzyValueTypeVoid, nullptr, 0}, nullptr, 0, host_fn, nullptr}};

FizzyValue mod1g1value{42};
FizzyExternalGlobal mod1g1 = {&mod1g1value, {FizzyValueTypeI32, false}};
Expand Down Expand Up @@ -1428,12 +1436,12 @@ TEST(capi, execute_with_host_function)
const FizzyValueType inputs[] = {FizzyValueTypeI32, FizzyValueTypeI32};

FizzyExternalFunction host_funcs[] = {
{{FizzyValueTypeI32, nullptr, 0},
{{FizzyValueTypeI32, nullptr, 0}, nullptr, 0,
[](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) noexcept {
return FizzyExecutionResult{false, true, {42}};
},
nullptr},
{{FizzyValueTypeI32, &inputs[0], 2},
{{FizzyValueTypeI32, &inputs[0], 2}, nullptr, 0,
[](void*, FizzyInstance*, const FizzyValue* args, FizzyExecutionContext*) noexcept {
FizzyValue v;
v.i32 = args[0].i32 / args[1].i32;
Expand Down Expand Up @@ -1466,7 +1474,7 @@ TEST(capi, imported_function_traps)
auto module = fizzy_parse(wasm.data(), wasm.size(), nullptr);
ASSERT_NE(module, nullptr);

FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0},
FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0}, nullptr, 0,
[](void*, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) noexcept {
return FizzyExecutionResult{true, false, {}};
},
Expand Down Expand Up @@ -1495,7 +1503,7 @@ TEST(capi, imported_function_void)
ASSERT_NE(module, nullptr);

bool called = false;
FizzyExternalFunction host_funcs[] = {{{},
FizzyExternalFunction host_funcs[] = {{{}, nullptr, 0,
[](void* context, FizzyInstance*, const FizzyValue*, FizzyExecutionContext*) noexcept {
*static_cast<bool*>(context) = true;
return FizzyExecutionResult{false, false, {}};
Expand Down Expand Up @@ -1558,7 +1566,6 @@ TEST(capi, imported_function_from_another_module)
FizzyValue args[] = {{44}, {2}};
EXPECT_THAT(fizzy_execute(instance2, 1, args), CResult(42_u32));

fizzy_free_exported_function(&func);
fizzy_free_instance(instance2);
fizzy_free_instance(instance1);
}
Expand Down Expand Up @@ -1936,7 +1943,8 @@ TEST(capi, import_name_after_instantiate)
EXPECT_STREQ(import0.module, "m");
EXPECT_STREQ(import0.name, "f1");

FizzyExternalFunction host_funcs[] = {{{FizzyValueTypeI32, nullptr, 0}, NullFn, nullptr}};
FizzyExternalFunction host_funcs[] = {
{{FizzyValueTypeI32, nullptr, 0}, nullptr, 0, NullFn, nullptr}};

auto instance = fizzy_instantiate(
module, host_funcs, 1, nullptr, nullptr, nullptr, 0, FizzyMemoryPagesLimitDefault, nullptr);
Expand Down
Loading