summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt20
-rw-r--r--include/wabt/sha256.h31
-rw-r--r--src/c-writer.cc158
-rw-r--r--src/config.h.in3
-rw-r--r--src/sha256.cc48
-rw-r--r--src/template/wasm2c.declarations.c30
-rw-r--r--src/template/wasm2c.includes.c1
-rwxr-xr-xtest/run-spec-wasm2c.py2
-rw-r--r--test/spec-wasm2c-prefix.c9
-rw-r--r--test/wasm2c/add.txt61
-rw-r--r--test/wasm2c/hello.txt89
-rw-r--r--test/wasm2c/minimal.txt45
m---------third_party/picosha20
-rw-r--r--wasm2c/.gitignore4
-rw-r--r--wasm2c/README.md62
-rw-r--r--wasm2c/examples/callback/Makefile19
-rw-r--r--wasm2c/examples/callback/callback.wat19
-rw-r--r--wasm2c/examples/callback/main.c39
-rw-r--r--wasm2c/examples/fac/fac.c59
-rw-r--r--wasm2c/examples/fac/fac.h2
-rw-r--r--wasm2c/examples/fac/main.c4
-rw-r--r--wasm2c/examples/rot13/main.c3
-rw-r--r--wasm2c/wasm-rt-impl.c72
-rw-r--r--wasm2c/wasm-rt.h36
25 files changed, 528 insertions, 291 deletions
diff --git a/.gitmodules b/.gitmodules
index 15d95622..bb8a7a6e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
[submodule "third_party/uvwasi"]
path = third_party/uvwasi
url = https://github.com/nodejs/uvwasi
+[submodule "third_party/picosha2"]
+ path = third_party/picosha2
+ url = https://github.com/okdshin/PicoSHA2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dfa3cfc1..c02bebf4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ option(WABT_INSTALL_RULES "Include WABT's install() rules" "${PROJECT_IS_TOP_LEV
# WASI support is still a work in progress.
# Only a handful of syscalls are supported at this point.
option(WITH_WASI "Build WASI support via uvwasi" OFF)
+option(USE_INTERNAL_SHA256 "Use internal PicoSHA2 for SHA-256 support instead of OpenSSL libcrypto" OFF)
if (MSVC)
set(COMPILER_IS_CLANG 0)
@@ -100,6 +101,17 @@ check_include_file("unistd.h" HAVE_UNISTD_H)
check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
check_symbol_exists(strcasecmp "strings.h" HAVE_STRCASECMP)
+if (NOT USE_INTERNAL_SHA256)
+ find_package(OpenSSL QUIET)
+ if (OpenSSL_FOUND)
+ set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ check_include_file("openssl/sha.h" HAVE_OPENSSL_SHA_H)
+ if (HAVE_OPENSSL_SHA_H)
+ find_package_message(OpenSSL "Using OpenSSL libcrypto for SHA-256" "${HAVE_OPENSSL_SHA_H}")
+ endif()
+ endif()
+endif()
+
if (WIN32)
check_symbol_exists(ENABLE_VIRTUAL_TERMINAL_PROCESSING "windows.h" HAVE_WIN32_VT100)
endif ()
@@ -282,6 +294,7 @@ set(WABT_LIBRARY_CC
src/opcode.cc
src/option-parser.cc
src/resolve-names.cc
+ src/sha256.cc
src/shared-validator.cc
src/stream.cc
src/token.cc
@@ -334,6 +347,7 @@ set(WABT_LIBRARY_H
include/wabt/opcode.h
include/wabt/option-parser.h
include/wabt/resolve-names.h
+ include/wabt/sha256.h
include/wabt/shared-validator.h
include/wabt/stream.h
include/wabt/string-util.h
@@ -361,6 +375,12 @@ set(WABT_LIBRARY_SRC ${WABT_LIBRARY_CC} ${WABT_LIBRARY_H})
add_library(wabt STATIC ${WABT_LIBRARY_SRC})
add_library(wabt::wabt ALIAS wabt)
+if (HAVE_OPENSSL_SHA_H)
+ target_link_libraries(wabt OpenSSL::Crypto)
+else()
+ include_directories("${WABT_SOURCE_DIR}/third_party/picosha2")
+endif()
+
target_compile_features(wabt PUBLIC cxx_std_17)
target_include_directories(
wabt
diff --git a/include/wabt/sha256.h b/include/wabt/sha256.h
new file mode 100644
index 00000000..744e2d1c
--- /dev/null
+++ b/include/wabt/sha256.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_SHA256_H_
+#define WABT_SHA256_H_
+
+#include "wabt/config.h"
+
+#include <string>
+#include <string_view>
+
+namespace wabt {
+
+void sha256(std::string_view input, std::string& digest);
+
+} // namespace wabt
+
+#endif // WABT_SHA256_H_
diff --git a/src/c-writer.cc b/src/c-writer.cc
index b2aaa869..e7e8d996 100644
--- a/src/c-writer.cc
+++ b/src/c-writer.cc
@@ -27,6 +27,7 @@
#include "wabt/common.h"
#include "wabt/ir.h"
#include "wabt/literal.h"
+#include "wabt/sha256.h"
#include "wabt/stream.h"
#include "wabt/string-util.h"
@@ -141,6 +142,11 @@ struct TryCatchLabel {
bool used;
};
+struct FuncTypeExpr {
+ const FuncType* func_type;
+ FuncTypeExpr(const FuncType* f) : func_type(f) {}
+};
+
struct Newline {};
struct OpenBrace {};
struct CloseBrace {};
@@ -212,6 +218,8 @@ class CWriter {
std::string DefineLocalScopeName(std::string_view);
std::string DefineStackVarName(Index, Type, std::string_view);
+ static void SerializeFuncType(const FuncType&, std::string&);
+
void Indent(int size = INDENT_SIZE);
void Dedent(int size = INDENT_SIZE);
void WriteIndent();
@@ -261,6 +269,7 @@ class CWriter {
void WriteMultivalueTypes();
void WriteTagTypes();
void WriteFuncTypes();
+ void Write(const FuncTypeExpr&);
void WriteTags();
void ComputeUniqueImports();
void BeginInstance();
@@ -292,8 +301,10 @@ class CWriter {
void WriteExports(CWriterPhase);
void WriteInitDecl();
void WriteFreeDecl();
+ void WriteGetFuncTypeDecl();
void WriteInit();
void WriteFree();
+ void WriteGetFuncType();
void WriteInitInstanceImport();
void WriteImportProperties(CWriterPhase);
void WriteFuncs();
@@ -372,6 +383,8 @@ class CWriter {
std::vector<std::pair<std::string, MemoryStream>> func_sections_;
SymbolSet func_includes_;
+
+ std::vector<std::string> unique_func_type_names_;
};
constexpr std::string_view kImplicitFuncLabel = "$Bfunc";
@@ -925,7 +938,6 @@ void CWriter::Write(const Const& const_) {
}
void CWriter::WriteInitDecl() {
- Write("void " + module_prefix_ + "_init_module(void);", Newline());
Write("void " + module_prefix_ + "_instantiate(", ModuleInstanceTypeName(),
"*");
for (const auto& import_module_name : import_module_set_) {
@@ -939,6 +951,12 @@ void CWriter::WriteFreeDecl() {
Newline());
}
+void CWriter::WriteGetFuncTypeDecl() {
+ Write("wasm_rt_func_type_t ", module_prefix_,
+ "_get_func_type(uint32_t param_count, uint32_t result_count, ...);",
+ Newline());
+}
+
void CWriter::WriteInitExpr(const ExprList& expr_list) {
if (expr_list.empty())
return;
@@ -959,9 +977,9 @@ void CWriter::WriteInitExpr(const ExprList& expr_list) {
const FuncDeclaration& decl = func->decl;
assert(decl.has_func_type);
- Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
- Write("(wasm_rt_funcref_t){func_types[", func_type_index, "], ",
+ Write("(wasm_rt_funcref_t){", FuncTypeExpr(func_type), ", ",
"(wasm_rt_function_ptr_t)",
ExternalPtr(ModuleFieldType::Func, func->name), ", ");
@@ -1055,29 +1073,61 @@ void CWriter::WriteFuncTypes() {
}
Write(Newline());
- Writef("static u32 func_types[%" PRIzd "];", module_->types.size());
- Write(Newline());
- Write(Newline(), "static void init_func_types(void) ", OpenBrace());
- Index func_type_index = 0;
- for (TypeEntry* type : module_->types) {
- FuncType* func_type = cast<FuncType>(type);
- Index num_params = func_type->GetNumParams();
- Index num_results = func_type->GetNumResults();
- Write("func_types[", func_type_index, "] = wasm_rt_register_func_type(",
- num_params, ", ", num_results);
- for (Index i = 0; i < num_params; ++i) {
- Write(", ", TypeEnum(func_type->GetParamType(i)));
- }
+ std::unordered_map<std::string, std::string> type_hash;
- for (Index i = 0; i < num_results; ++i) {
- Write(", ", TypeEnum(func_type->GetResultType(i)));
+ std::string serialized_type;
+ for (const TypeEntry* type : module_->types) {
+ const std::string name =
+ DefineGlobalScopeName(ModuleFieldType::Type, type->name);
+ SerializeFuncType(*cast<FuncType>(type), serialized_type);
+
+ auto prior_type = type_hash.find(serialized_type);
+ if (prior_type != type_hash.end()) {
+ /* duplicate function type */
+ unique_func_type_names_.push_back(prior_type->second);
+ } else {
+ unique_func_type_names_.push_back(name);
+ type_hash.emplace(serialized_type, name);
+ Write("FUNC_TYPE_T(", name, ") = \"");
+ for (uint8_t x : serialized_type) {
+ Writef("\\x%02x", x);
+ }
+ Write("\";", Newline());
}
+ }
+}
- Write(");", Newline());
- ++func_type_index;
+void CWriter::Write(const FuncTypeExpr& expr) {
+ Index func_type_index = module_->GetFuncTypeIndex(expr.func_type->sig);
+ Write(unique_func_type_names_.at(func_type_index));
+}
+
+// static
+void CWriter::SerializeFuncType(const FuncType& func_type,
+ std::string& serialized_type) {
+ unsigned int len = func_type.GetNumParams() + func_type.GetNumResults() + 1;
+
+ char* const mangled_signature = static_cast<char*>(alloca(len));
+ char* next_byte = mangled_signature;
+
+ // step 1: serialize each param type
+ for (Index i = 0; i < func_type.GetNumParams(); ++i) {
+ *next_byte++ = MangleType(func_type.GetParamType(i));
}
- Write(CloseBrace(), Newline());
+
+ // step 2: separate params and results with a space
+ *next_byte++ = ' ';
+
+ // step 3: serialize each result type
+ for (Index i = 0; i < func_type.GetNumResults(); ++i) {
+ *next_byte++ = MangleType(func_type.GetResultType(i));
+ }
+
+ assert(next_byte - mangled_signature == len);
+
+ // step 4: SHA-256 the whole string
+ sha256({mangled_signature, len}, serialized_type);
}
void CWriter::WriteTags() {
@@ -1540,10 +1590,8 @@ void CWriter::WriteElemInitializers() {
switch (expr.type()) {
case ExprType::RefFunc: {
const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var);
- const Index func_type_index =
- module_->GetFuncTypeIndex(func->decl.type_var);
- Write("{", func_type_index, ", ");
- Write("(wasm_rt_function_ptr_t)",
+ const FuncType* func_type = module_->GetFuncType(func->decl.type_var);
+ Write("{", FuncTypeExpr(func_type), ", (wasm_rt_function_ptr_t)",
ExternalPtr(ModuleFieldType::Func, func->name), ", ");
const bool is_import = import_module_sym_map_.count(func->name) != 0;
if (is_import) {
@@ -1556,7 +1604,7 @@ void CWriter::WriteElemInitializers() {
Write("},", Newline());
} break;
case ExprType::RefNull:
- Write("{0, NULL, 0},", Newline());
+ Write("{NULL, NULL, 0},", Newline());
break;
default:
WABT_UNREACHABLE;
@@ -1646,8 +1694,7 @@ void CWriter::WriteElemTableInit(bool active_initialization,
}
if (dst_table->elem_type == Type::FuncRef) {
- Write(", instance, ",
- src_segment->elem_exprs.empty() ? "NULL" : "func_types");
+ Write(", instance");
}
Write(");", Newline());
@@ -1788,15 +1835,6 @@ void CWriter::WriteExports(CWriterPhase kind) {
}
void CWriter::WriteInit() {
- Write(Newline(), "void " + module_prefix_ + "_init_module(void) ",
- OpenBrace());
- Write("assert(wasm_rt_is_initialized());", Newline());
- Write("s_module_initialized = true;", Newline());
- if (!module_->types.empty()) {
- Write("init_func_types();", Newline());
- }
- Write(CloseBrace(), Newline());
-
Write(Newline(), "void " + module_prefix_ + "_instantiate(",
ModuleInstanceTypeName(), "* instance");
for (const auto& import_module_name : import_module_set_) {
@@ -1806,7 +1844,6 @@ void CWriter::WriteInit() {
Write(") ", OpenBrace());
Write("assert(wasm_rt_is_initialized());", Newline());
- Write("assert(s_module_initialized);", Newline());
if (!import_module_set_.empty()) {
Write("init_instance_import(instance");
@@ -1849,6 +1886,39 @@ void CWriter::WriteInit() {
Write(CloseBrace(), Newline());
}
+void CWriter::WriteGetFuncType() {
+ Write(Newline(), "wasm_rt_func_type_t ", module_prefix_,
+ "_get_func_type(uint32_t param_count, uint32_t result_count, ...) ",
+ OpenBrace());
+
+ Write("va_list args;", Newline());
+
+ for (const TypeEntry* type : module_->types) {
+ const FuncType* func_type = cast<FuncType>(type);
+ const FuncSignature& signature = func_type->sig;
+
+ Write(Newline(), "if (param_count == ", signature.GetNumParams(),
+ " && result_count == ", signature.GetNumResults(), ") ", OpenBrace());
+ Write("va_start(args, result_count);", Newline());
+ Write("if (true");
+ for (const auto& t : signature.param_types) {
+ Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t));
+ }
+ for (const auto& t : signature.result_types) {
+ Write(" && va_arg(args, wasm_rt_type_t) == ", TypeEnum(t));
+ }
+ Write(") ", OpenBrace());
+ Write("va_end(args);", Newline());
+ Write("return ", FuncTypeExpr(func_type), ";", Newline());
+ Write(CloseBrace(), Newline());
+ Write("va_end(args);", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ Write(Newline(), "return NULL;", Newline());
+ Write(CloseBrace(), Newline());
+}
+
void CWriter::WriteInitInstanceImport() {
if (import_module_set_.empty())
return;
@@ -2437,12 +2507,12 @@ void CWriter::Write(const ExprList& exprs) {
module_->GetTable(cast<CallIndirectExpr>(&expr)->table);
assert(decl.has_func_type);
- Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
Write("CALL_INDIRECT(",
ExternalInstanceRef(ModuleFieldType::Table, table->name), ", ");
WriteCallIndirectFuncDeclaration(decl, "(*)");
- Write(", ", func_type_index, ", ", StackVar(0));
+ Write(", ", FuncTypeExpr(func_type), ", ", StackVar(0));
Write(", ", ExternalInstanceRef(ModuleFieldType::Table, table->name),
".data[", StackVar(0), "].module_instance");
for (Index i = 0; i < num_params; ++i) {
@@ -2717,10 +2787,10 @@ void CWriter::Write(const ExprList& exprs) {
const FuncDeclaration& decl = func->decl;
assert(decl.has_func_type);
- Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+ const FuncType* func_type = module_->GetFuncType(decl.type_var);
- Write(StackVar(0), " = (wasm_rt_funcref_t){func_types[",
- func_type_index, "], (wasm_rt_function_ptr_t)",
+ Write(StackVar(0), " = (wasm_rt_funcref_t){", FuncTypeExpr(func_type),
+ ", (wasm_rt_function_ptr_t)",
ExternalPtr(ModuleFieldType::Func, func->name), ", ");
bool is_import = import_module_sym_map_.count(func->name) != 0;
@@ -3577,6 +3647,7 @@ void CWriter::WriteCHeader() {
WriteModuleInstance();
WriteInitDecl();
WriteFreeDecl();
+ WriteGetFuncTypeDecl();
WriteMultivalueTypes();
WriteImports();
WriteImportProperties(CWriterPhase::Declarations);
@@ -3602,6 +3673,7 @@ void CWriter::WriteCSource() {
WriteImportProperties(CWriterPhase::Definitions);
WriteInit();
WriteFree();
+ WriteGetFuncType();
}
Result CWriter::WriteModule(const Module& module) {
diff --git a/src/config.h.in b/src/config.h.in
index afba03b0..5eb8e0a6 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -47,6 +47,9 @@
/* Whether the target architecture is big endian */
#cmakedefine01 WABT_BIG_ENDIAN
+/* Whether <openssl/sha.h> is available */
+#cmakedefine01 HAVE_OPENSSL_SHA_H
+
#cmakedefine01 COMPILER_IS_CLANG
#cmakedefine01 COMPILER_IS_GNU
#cmakedefine01 COMPILER_IS_MSVC
diff --git a/src/sha256.cc b/src/sha256.cc
new file mode 100644
index 00000000..efcb27a1
--- /dev/null
+++ b/src/sha256.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/sha256.h"
+
+#if HAVE_OPENSSL_SHA_H
+#include <openssl/sha.h>
+#else
+#include "picosha2.h"
+#endif
+
+namespace wabt {
+
+/**
+ * SHA-256 the "input" sv into the output "digest".
+ *
+ * Uses OpenSSL's libcrypto or vendored PicoSHA2.
+ */
+void sha256(std::string_view input, std::string& digest) {
+ digest.clear();
+
+#if HAVE_OPENSSL_SHA_H
+ digest.resize(SHA256_DIGEST_LENGTH);
+ if (!SHA256(reinterpret_cast<const uint8_t*>(input.data()), input.size(),
+ reinterpret_cast<uint8_t*>(digest.data()))) {
+ // should not be possible to fail here, but check (and abort) just in case
+ abort();
+ }
+#else
+ digest.resize(picosha2::k_digest_size);
+ picosha2::hash256(input, digest);
+#endif
+}
+
+} // namespace wabt
diff --git a/src/template/wasm2c.declarations.c b/src/template/wasm2c.declarations.c
index 0546b57d..de5f7899 100644
--- a/src/template/wasm2c.declarations.c
+++ b/src/template/wasm2c.declarations.c
@@ -15,10 +15,15 @@
#define UNREACHABLE TRAP(UNREACHABLE)
-#define CALL_INDIRECT(table, t, ft, x, ...) \
- (LIKELY((x) < table.size && table.data[x].func && \
- table.data[x].func_type == func_types[ft]) || \
- TRAP(CALL_INDIRECT), \
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+}
+
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+ (LIKELY((x) < table.size && table.data[x].func && \
+ func_types_eq(ft, table.data[x].func_type)) || \
+ TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#ifdef SUPPORT_MEMORY64
@@ -480,7 +485,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
}
typedef struct {
- uint32_t func_type_index;
+ wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
size_t module_offset;
} wasm_elem_segment_expr_t;
@@ -491,17 +496,16 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
u32 dest_addr,
u32 src_addr,
u32 n,
- void* module_instance,
- const u32* func_types) {
+ void* module_instance) {
if (UNLIKELY(src_addr + (uint64_t)n > src_size))
TRAP(OOB);
if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
- dest->data[dest_addr + i] = (wasm_rt_funcref_t){
- func_types[src_expr->func_type_index], src_expr->func,
- (char*)module_instance + src_expr->module_offset};
+ dest->data[dest_addr + i] =
+ (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+ (char*)module_instance + src_expr->module_offset};
}
}
@@ -572,4 +576,8 @@ DEFINE_TABLE_SET(externref)
DEFINE_TABLE_FILL(funcref)
DEFINE_TABLE_FILL(externref)
-static bool s_module_initialized = false;
+#if defined(__GNUC__) || defined(__clang__)
+#define FUNC_TYPE_T(x) static const char* const x
+#else
+#define FUNC_TYPE_T(x) static const char x[]
+#endif
diff --git a/src/template/wasm2c.includes.c b/src/template/wasm2c.includes.c
index e020fd5c..85e4edb9 100644
--- a/src/template/wasm2c.includes.c
+++ b/src/template/wasm2c.includes.c
@@ -1,5 +1,6 @@
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#if defined(_MSC_VER)
diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py
index 0714e249..e9a93228 100755
--- a/test/run-spec-wasm2c.py
+++ b/test/run-spec-wasm2c.py
@@ -253,7 +253,6 @@ class CWriter(object):
def _WriteModuleCommand(self, command):
self.module_idx += 1
- self.out_file.write('%s_init_module();\n' % self.GetModulePrefix())
self._WriteModuleInitCall(command, False)
def _WriteModuleInstances(self):
@@ -269,7 +268,6 @@ class CWriter(object):
def _WriteAssertUninstantiableCommand(self, command):
self.module_idx += 1
- self.out_file.write('%s_init_module();\n' % self.GetModulePrefix())
self._WriteModuleInitCall(command, True)
def _WriteActionCommand(self, command):
diff --git a/test/spec-wasm2c-prefix.c b/test/spec-wasm2c-prefix.c
index b9ba0934..c3c6e500 100644
--- a/test/spec-wasm2c-prefix.c
+++ b/test/spec-wasm2c-prefix.c
@@ -194,10 +194,15 @@ static bool is_equal_wasm_rt_externref_t(wasm_rt_externref_t x,
return x == y;
}
+static inline bool is_equal_wasm_rt_func_type_t(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || (a && b && !memcmp(a, b, 32));
+}
+
static bool is_equal_wasm_rt_funcref_t(wasm_rt_funcref_t x,
wasm_rt_funcref_t y) {
- return (x.func_type == y.func_type) && (x.func == y.func) &&
- (x.module_instance == y.module_instance);
+ return is_equal_wasm_rt_func_type_t(x.func_type, y.func_type) &&
+ (x.func == y.func) && (x.module_instance == y.module_instance);
}
wasm_rt_externref_t spectest_make_externref(uintptr_t x) {
diff --git a/test/wasm2c/add.txt b/test/wasm2c/add.txt
index 6f005f6d..f9207db3 100644
--- a/test/wasm2c/add.txt
+++ b/test/wasm2c/add.txt
@@ -35,9 +35,9 @@ typedef struct Z_test_instance_t {
char dummy_member;
} Z_test_instance_t;
-void Z_test_init_module(void);
void Z_test_instantiate(Z_test_instance_t*);
void Z_test_free(Z_test_instance_t*);
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...);
/* export: 'add' */
u32 Z_testZ_add(Z_test_instance_t*, u32, u32);
@@ -50,6 +50,7 @@ u32 Z_testZ_add(Z_test_instance_t*, u32, u32);
/* Automatically generated by wasm2c */
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#if defined(_MSC_VER)
@@ -78,10 +79,15 @@ u32 Z_testZ_add(Z_test_instance_t*, u32, u32);
#define UNREACHABLE TRAP(UNREACHABLE)
-#define CALL_INDIRECT(table, t, ft, x, ...) \
- (LIKELY((x) < table.size && table.data[x].func && \
- table.data[x].func_type == func_types[ft]) || \
- TRAP(CALL_INDIRECT), \
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+}
+
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+ (LIKELY((x) < table.size && table.data[x].func && \
+ func_types_eq(ft, table.data[x].func_type)) || \
+ TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#ifdef SUPPORT_MEMORY64
@@ -543,7 +549,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
}
typedef struct {
- uint32_t func_type_index;
+ wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
size_t module_offset;
} wasm_elem_segment_expr_t;
@@ -554,17 +560,16 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
u32 dest_addr,
u32 src_addr,
u32 n,
- void* module_instance,
- const u32* func_types) {
+ void* module_instance) {
if (UNLIKELY(src_addr + (uint64_t)n > src_size))
TRAP(OOB);
if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
- dest->data[dest_addr + i] = (wasm_rt_funcref_t){
- func_types[src_expr->func_type_index], src_expr->func,
- (char*)module_instance + src_expr->module_offset};
+ dest->data[dest_addr + i] =
+ (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+ (char*)module_instance + src_expr->module_offset};
}
}
@@ -635,13 +640,13 @@ DEFINE_TABLE_SET(externref)
DEFINE_TABLE_FILL(funcref)
DEFINE_TABLE_FILL(externref)
-static bool s_module_initialized = false;
-
-static u32 func_types[1];
+#if defined(__GNUC__) || defined(__clang__)
+#define FUNC_TYPE_T(x) static const char* const x
+#else
+#define FUNC_TYPE_T(x) static const char x[]
+#endif
-static void init_func_types(void) {
- func_types[0] = wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32);
-}
+FUNC_TYPE_T(w2c_t0) = "\x92\xfb\x6a\xdf\x49\x07\x0a\x83\xbe\x08\x02\x68\xcd\xf6\x95\x27\x4a\xc2\xf3\xe5\xe4\x7d\x29\x49\xe8\xed\x42\x92\x6a\x9d\xda\xf0";
static u32 w2c_add(Z_test_instance_t*, u32, u32);
@@ -660,17 +665,25 @@ u32 Z_testZ_add(Z_test_instance_t* instance, u32 w2c_p0, u32 w2c_p1) {
return w2c_add(instance, w2c_p0, w2c_p1);
}
-void Z_test_init_module(void) {
- assert(wasm_rt_is_initialized());
- s_module_initialized = true;
- init_func_types();
-}
-
void Z_test_instantiate(Z_test_instance_t* instance) {
assert(wasm_rt_is_initialized());
- assert(s_module_initialized);
}
void Z_test_free(Z_test_instance_t* instance) {
}
+
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...) {
+ va_list args;
+
+ if (param_count == 2 && result_count == 1) {
+ va_start(args, result_count);
+ if (true && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32) {
+ va_end(args);
+ return w2c_t0;
+ }
+ va_end(args);
+ }
+
+ return NULL;
+}
;;; STDOUT ;;)
diff --git a/test/wasm2c/hello.txt b/test/wasm2c/hello.txt
index 0e3f0f26..91a55482 100644
--- a/test/wasm2c/hello.txt
+++ b/test/wasm2c/hello.txt
@@ -57,9 +57,9 @@ typedef struct Z_test_instance_t {
wasm_rt_funcref_table_t w2c_T0;
} Z_test_instance_t;
-void Z_test_init_module(void);
void Z_test_instantiate(Z_test_instance_t*, struct Z_wasi_snapshot_preview1_instance_t*);
void Z_test_free(Z_test_instance_t*);
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...);
/* import: 'wasi_snapshot_preview1' 'fd_write' */
u32 Z_wasi_snapshot_preview1Z_fd_write(struct Z_wasi_snapshot_preview1_instance_t*, u32, u32, u32, u32);
@@ -80,6 +80,7 @@ void Z_testZ__start(Z_test_instance_t*);
/* Automatically generated by wasm2c */
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#if defined(_MSC_VER)
@@ -108,10 +109,15 @@ void Z_testZ__start(Z_test_instance_t*);
#define UNREACHABLE TRAP(UNREACHABLE)
-#define CALL_INDIRECT(table, t, ft, x, ...) \
- (LIKELY((x) < table.size && table.data[x].func && \
- table.data[x].func_type == func_types[ft]) || \
- TRAP(CALL_INDIRECT), \
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+}
+
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+ (LIKELY((x) < table.size && table.data[x].func && \
+ func_types_eq(ft, table.data[x].func_type)) || \
+ TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#ifdef SUPPORT_MEMORY64
@@ -573,7 +579,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
}
typedef struct {
- uint32_t func_type_index;
+ wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
size_t module_offset;
} wasm_elem_segment_expr_t;
@@ -584,17 +590,16 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
u32 dest_addr,
u32 src_addr,
u32 n,
- void* module_instance,
- const u32* func_types) {
+ void* module_instance) {
if (UNLIKELY(src_addr + (uint64_t)n > src_size))
TRAP(OOB);
if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
- dest->data[dest_addr + i] = (wasm_rt_funcref_t){
- func_types[src_expr->func_type_index], src_expr->func,
- (char*)module_instance + src_expr->module_offset};
+ dest->data[dest_addr + i] =
+ (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+ (char*)module_instance + src_expr->module_offset};
}
}
@@ -665,15 +670,15 @@ DEFINE_TABLE_SET(externref)
DEFINE_TABLE_FILL(funcref)
DEFINE_TABLE_FILL(externref)
-static bool s_module_initialized = false;
-
-static u32 func_types[3];
+#if defined(__GNUC__) || defined(__clang__)
+#define FUNC_TYPE_T(x) static const char* const x
+#else
+#define FUNC_TYPE_T(x) static const char x[]
+#endif
-static void init_func_types(void) {
- func_types[0] = wasm_rt_register_func_type(4, 1, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32, WASM_RT_I32);
- func_types[1] = wasm_rt_register_func_type(1, 0, WASM_RT_I32);
- func_types[2] = wasm_rt_register_func_type(0, 0);
-}
+FUNC_TYPE_T(w2c_t0) = "\xf6\x98\x1b\xc6\x10\xda\xb7\xb2\x63\x37\xcd\xdc\x72\xca\xe9\x50\x00\x13\xba\x10\x6c\xde\x87\x27\x10\xf8\x86\x2f\xe3\xdb\x94\xe4";
+FUNC_TYPE_T(w2c_t1) = "\x89\x3a\x3d\x2c\x8f\x4d\x7f\x6d\x6c\x9d\x62\x67\x29\xaf\x3d\x44\x39\x8e\xc3\xf3\xe8\x51\xc1\x99\xb9\xdd\x9f\xd5\x3d\x1f\xd3\xe4";
+FUNC_TYPE_T(w2c_t2) = "\x36\xa9\xe7\xf1\xc9\x5b\x82\xff\xb9\x97\x43\xe0\xc5\xc4\xce\x95\xd8\x3c\x9a\x43\x0a\xac\x59\xf8\x4e\xf3\xcb\xfa\xb6\x14\x50\x68";
static void w2c__start(Z_test_instance_t*);
@@ -690,12 +695,12 @@ static void init_memories(Z_test_instance_t* instance) {
static void init_data_instances(Z_test_instance_t *instance) {
}
static const wasm_elem_segment_expr_t elem_segment_exprs_w2c_e0[] = {
- {0, (wasm_rt_function_ptr_t)Z_wasi_snapshot_preview1Z_fd_write, offsetof(Z_test_instance_t, Z_wasi_snapshot_preview1_instance)},
+ {w2c_t0, (wasm_rt_function_ptr_t)Z_wasi_snapshot_preview1Z_fd_write, offsetof(Z_test_instance_t, Z_wasi_snapshot_preview1_instance)},
};
static void init_tables(Z_test_instance_t* instance) {
wasm_rt_allocate_funcref_table(&instance->w2c_T0, 1, 1);
- funcref_table_init(&instance->w2c_T0, elem_segment_exprs_w2c_e0, 1, 0u, 0, 1, instance, func_types);
+ funcref_table_init(&instance->w2c_T0, elem_segment_exprs_w2c_e0, 1, 0u, 0, 1, instance);
}
static void init_elem_instances(Z_test_instance_t *instance) {
@@ -715,7 +720,7 @@ static void w2c__start(Z_test_instance_t* instance) {
w2c_i2 = 1u;
w2c_i3 = 0u;
w2c_i4 = 0u;
- w2c_i0 = CALL_INDIRECT(instance->w2c_T0, u32 (*)(void*, u32, u32, u32, u32), 0, w2c_i4, instance->w2c_T0.data[w2c_i4].module_instance, w2c_i0, w2c_i1, w2c_i2, w2c_i3);
+ w2c_i0 = CALL_INDIRECT(instance->w2c_T0, u32 (*)(void*, u32, u32, u32, u32), w2c_t0, w2c_i4, instance->w2c_T0.data[w2c_i4].module_instance, w2c_i0, w2c_i1, w2c_i2, w2c_i3);
(*Z_wasi_snapshot_preview1Z_proc_exit)(instance->Z_wasi_snapshot_preview1_instance, w2c_i0);
FUNC_EPILOGUE;
}
@@ -734,15 +739,8 @@ static void init_instance_import(Z_test_instance_t* instance, struct Z_wasi_snap
instance->Z_wasi_snapshot_preview1_instance = Z_wasi_snapshot_preview1_instance;
}
-void Z_test_init_module(void) {
- assert(wasm_rt_is_initialized());
- s_module_initialized = true;
- init_func_types();
-}
-
void Z_test_instantiate(Z_test_instance_t* instance, struct Z_wasi_snapshot_preview1_instance_t* Z_wasi_snapshot_preview1_instance) {
assert(wasm_rt_is_initialized());
- assert(s_module_initialized);
init_instance_import(instance, Z_wasi_snapshot_preview1_instance);
init_memories(instance);
init_tables(instance);
@@ -754,4 +752,37 @@ void Z_test_free(Z_test_instance_t* instance) {
wasm_rt_free_funcref_table(&instance->w2c_T0);
wasm_rt_free_memory(&instance->w2c_memory);
}
+
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...) {
+ va_list args;
+
+ if (param_count == 4 && result_count == 1) {
+ va_start(args, result_count);
+ if (true && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32) {
+ va_end(args);
+ return w2c_t0;
+ }
+ va_end(args);
+ }
+
+ if (param_count == 1 && result_count == 0) {
+ va_start(args, result_count);
+ if (true && va_arg(args, wasm_rt_type_t) == WASM_RT_I32) {
+ va_end(args);
+ return w2c_t1;
+ }
+ va_end(args);
+ }
+
+ if (param_count == 0 && result_count == 0) {
+ va_start(args, result_count);
+ if (true) {
+ va_end(args);
+ return w2c_t2;
+ }
+ va_end(args);
+ }
+
+ return NULL;
+}
;;; STDOUT ;;)
diff --git a/test/wasm2c/minimal.txt b/test/wasm2c/minimal.txt
index 857dc879..6a706d40 100644
--- a/test/wasm2c/minimal.txt
+++ b/test/wasm2c/minimal.txt
@@ -32,9 +32,9 @@ typedef struct Z_test_instance_t {
char dummy_member;
} Z_test_instance_t;
-void Z_test_init_module(void);
void Z_test_instantiate(Z_test_instance_t*);
void Z_test_free(Z_test_instance_t*);
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...);
#ifdef __cplusplus
}
@@ -44,6 +44,7 @@ void Z_test_free(Z_test_instance_t*);
/* Automatically generated by wasm2c */
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#if defined(_MSC_VER)
@@ -72,10 +73,15 @@ void Z_test_free(Z_test_instance_t*);
#define UNREACHABLE TRAP(UNREACHABLE)
-#define CALL_INDIRECT(table, t, ft, x, ...) \
- (LIKELY((x) < table.size && table.data[x].func && \
- table.data[x].func_type == func_types[ft]) || \
- TRAP(CALL_INDIRECT), \
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+}
+
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+ (LIKELY((x) < table.size && table.data[x].func && \
+ func_types_eq(ft, table.data[x].func_type)) || \
+ TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#ifdef SUPPORT_MEMORY64
@@ -537,7 +543,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
}
typedef struct {
- uint32_t func_type_index;
+ wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
size_t module_offset;
} wasm_elem_segment_expr_t;
@@ -548,17 +554,16 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
u32 dest_addr,
u32 src_addr,
u32 n,
- void* module_instance,
- const u32* func_types) {
+ void* module_instance) {
if (UNLIKELY(src_addr + (uint64_t)n > src_size))
TRAP(OOB);
if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
- dest->data[dest_addr + i] = (wasm_rt_funcref_t){
- func_types[src_expr->func_type_index], src_expr->func,
- (char*)module_instance + src_expr->module_offset};
+ dest->data[dest_addr + i] =
+ (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+ (char*)module_instance + src_expr->module_offset};
}
}
@@ -629,18 +634,22 @@ DEFINE_TABLE_SET(externref)
DEFINE_TABLE_FILL(funcref)
DEFINE_TABLE_FILL(externref)
-static bool s_module_initialized = false;
-
-void Z_test_init_module(void) {
- assert(wasm_rt_is_initialized());
- s_module_initialized = true;
-}
+#if defined(__GNUC__) || defined(__clang__)
+#define FUNC_TYPE_T(x) static const char* const x
+#else
+#define FUNC_TYPE_T(x) static const char x[]
+#endif
void Z_test_instantiate(Z_test_instance_t* instance) {
assert(wasm_rt_is_initialized());
- assert(s_module_initialized);
}
void Z_test_free(Z_test_instance_t* instance) {
}
+
+wasm_rt_func_type_t Z_test_get_func_type(uint32_t param_count, uint32_t result_count, ...) {
+ va_list args;
+
+ return NULL;
+}
;;; STDOUT ;;)
diff --git a/third_party/picosha2 b/third_party/picosha2
new file mode 160000
+Subproject 27fcf6979298949e8a462e16d09a0351c18fcaf
diff --git a/wasm2c/.gitignore b/wasm2c/.gitignore
index 5d665f06..094559b6 100644
--- a/wasm2c/.gitignore
+++ b/wasm2c/.gitignore
@@ -5,3 +5,7 @@ examples/rot13/rot13
examples/rot13/rot13.c
examples/rot13/rot13.h
examples/rot13/rot13.wasm
+examples/callback/callback
+examples/callback/callback.c
+examples/callback/callback.h
+examples/callback/callback.wasm
diff --git a/wasm2c/README.md b/wasm2c/README.md
index b4fbe5fa..80a86952 100644
--- a/wasm2c/README.md
+++ b/wasm2c/README.md
@@ -49,8 +49,8 @@ files.
To actually use our `fac` module, we'll use create a new file, `main.c`, that
include `fac.h`, initializes the module, and calls `fac`.
-`wasm2c` generates a few C symbols based on the `fac.wasm` module: `Z_fac_init_module`, `Z_fac_instantiate`
-and `Z_facZ_fac`. The first initializes the module, the second constructs an instance of the module, and the third is the
+`wasm2c` generates a few C symbols based on the `fac.wasm` module: `Z_fac_instantiate`
+and `Z_facZ_fac`. The first constructs an instance of the module, and the second is the
exported `fac` function.
All the exported symbols shared a common prefix (`Z_fac`) which, by default, is
@@ -80,10 +80,6 @@ int main(int argc, char** argv) {
/* Initialize the Wasm runtime. */
wasm_rt_init();
- /* Initialize the `fac` module (this registers the module's function types in
- * a global data structure) */
- Z_fac_init_module();
-
/* Declare an instance of the `fac` module. */
Z_fac_instance_t instance;
@@ -170,7 +166,6 @@ typedef struct Z_fac_instance_t {
char dummy_member;
} Z_fac_instance_t;
-void Z_fac_init_module(void);
void Z_fac_instantiate(Z_fac_instance_t*);
void Z_fac_free(Z_fac_instance_t*);
@@ -210,7 +205,7 @@ typedef enum {
```
Next is the `wasm_rt_type_t` enum, which is used for specifying function
-signatures. The four WebAssembly value types are included:
+signatures. Six WebAssembly value types are included:
```c
typedef enum {
@@ -218,28 +213,35 @@ typedef enum {
WASM_RT_I64,
WASM_RT_F32,
WASM_RT_F64,
+ WASM_RT_FUNCREF,
+ WASM_RT_EXTERNREF,
} wasm_rt_type_t;
-```
-Next is `wasm_rt_funcref_t`, the function signature for a generic function
+Next is `wasm_rt_function_ptr_t`, the function signature for a generic function
callback. Since a WebAssembly table can contain functions of any given
signature, it is necessary to convert them to a canonical form:
```c
-typedef void (*wasm_rt_funcref_t)(void);
+typedef void (*wasm_rt_function_ptr_t)(void);
```
-Next are the definitions for a table element. `func_type` is a function index
-as returned by `wasm_rt_register_func_type` described below. `module_instance`
-is the pointer to the module instance that should be passed in when the func is
+Next is the definition for a function reference (in WebAssembly 1.0,
+this was the type of all table elements, but funcrefs can now also be
+used as ordinary values, and tables can alternately be declared as
+type externref). In this structure, `wasm_rt_func_type_t` is an opaque
+256-bit ID that can be looked up via the `Z_[modname]_get_func_type`
+function. (A demonstration of this can be found in the `callback`
+example.) `module_instance` is the pointer to the function's
+originating module instance, which will be passed in when the func is
called.
```c
typedef struct {
- uint32_t func_type;
- wasm_rt_funcref_t func;
+ wasm_rt_func_type_t func_type;
+ wasm_rt_function_ptr_t func;
void* module_instance;
-} wasm_rt_elem_t;
+} wasm_rt_funcref_t;
+
```
Next is the definition of a memory instance. The `data` field is a pointer to
@@ -263,10 +265,10 @@ limit.
```c
typedef struct {
- wasm_rt_elem_t* data;
+ wasm_rt_funcref_t* data;
uint32_t max_size;
uint32_t size;
-} wasm_rt_table_t;
+} wasm_rt_funcref_table_t;
```
## Symbols that must be defined by the embedder
@@ -283,7 +285,6 @@ bool wasm_rt_is_initialized(void);
void wasm_rt_free(void);
void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn));
const char* wasm_rt_strerror(wasm_rt_trap_t trap);
-uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...);
void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64);
uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages);
void wasm_rt_free_memory(wasm_rt_memory_t*);
@@ -310,13 +311,6 @@ wasm2c_custom_trap_handler`. It is recommended that you add this macro
definition via a compiler flag
(`-DWASM_RT_MEMCHECK_SIGNAL_HANDLER=wasm2c_custom_trap_handler` on clang/gcc).
-`wasm_rt_register_func_type` is a function that registers a function type. It
-is a variadic function where the first two arguments give the number of
-parameters and results, and the following arguments are the types. For example,
-the function `func (param i32 f32) (result f64)` would register the function
-type as
-`wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_F64)`.
-
`wasm_rt_allocate_memory` initializes a memory instance, and allocates at least
enough space for the given number of initial pages. The memory must be cleared
to zero. The `is64` parameter indicates if the memory is indexed with
@@ -386,21 +380,26 @@ must be of type `WASM_RT_UNWIND_TARGET`.
Finally, `fac.h` defines the module instance type (which in the case
of `fac` is essentially empty), and the exported symbols provided by
the module. In our example, the only function we exported was
-`fac`. `Z_fac_init_module()` initializes the whole module and must be
-called before any instance of the module is used.
+`fac`.
`Z_fac_instantiate(Z_fac_instance_t*)` creates an instance of
the module and must be called before the module instance can be
used. `Z_fac_free(Z_fac_instance_t*)` frees the instance.
+`Z_fac_get_func_type` can be used to look up a function type ID
+at runtime. It is a variadic function where the first two arguments
+give the number of parameters and results, and the following arguments
+are the types from the wasm_rt_type_t enum described above. The
+`callback` example demonstrates using this to pass a host function to
+a WebAssembly module dynamically at runtime.
```c
typedef struct Z_fac_instance_t {
char dummy_member;
} Z_fac_instance_t;
-void Z_fac_init_module(void);
void Z_fac_instantiate(Z_fac_instance_t*);
void Z_fac_free(Z_fac_instance_t*);
+wasm_rt_func_type_t Z_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...);
/* export: 'fac' */
u32 Z_facZ_fac(Z_fac_instance_t*, u32);
@@ -578,9 +577,6 @@ int main(int argc, char** argv) {
/* Initialize the Wasm runtime. */
wasm_rt_init();
- /* Initialize the rot13 module. */
- Z_rot13_init_module();
-
/* Declare two instances of the `rot13` module. */
Z_rot13_instance_t rot13_instance_1;
Z_rot13_instance_t rot13_instance_2;
diff --git a/wasm2c/examples/callback/Makefile b/wasm2c/examples/callback/Makefile
new file mode 100644
index 00000000..c226ff31
--- /dev/null
+++ b/wasm2c/examples/callback/Makefile
@@ -0,0 +1,19 @@
+# Use implicit rules for compiling C files.
+CFLAGS=-I../..
+
+all: callback
+
+callback: main.o callback.o ../../wasm-rt-impl.o
+
+clean:
+ rm -rf callback callback.wasm callback.c callback.h *.o
+
+callback: main.o callback.o ../../wasm-rt-impl.o -lm
+
+callback.wasm: callback.wat ../../../bin/wat2wasm
+ ../../../bin/wat2wasm --debug-names $< -o $@
+
+callback.c: callback.wasm ../../../bin/wasm2c
+ ../../../bin/wasm2c $< -o $@
+
+.PHONY: all clean
diff --git a/wasm2c/examples/callback/callback.wat b/wasm2c/examples/callback/callback.wat
new file mode 100644
index 00000000..6a8ab233
--- /dev/null
+++ b/wasm2c/examples/callback/callback.wat
@@ -0,0 +1,19 @@
+;; Module demonstrating use of a host-installed callback function.
+
+;; The type of the callback function. The type ID can be looked up outside the module by calling
+;; Z_[modname]_get_func_type(1, 0, WASM_RT_I32) (indicating 1 param, 0 results, param type is i32).
+(type $print_type (func (param i32)))
+
+;; An indirect function table to hold the callback function
+(table $table 1 funcref)
+
+;; A memory holding the string to be printed
+(memory (export "memory") (data "Hello, world.\00"))
+
+;; Allow the host to set the callback function
+(func (export "set_print_function") (param funcref)
+ (table.set $table (i32.const 0) (local.get 0)))
+
+;; Call the callback function with the location of "Hello, world."
+(func (export "say_hello")
+ (call_indirect (type $print_type) (i32.const 0) (i32.const 0)))
diff --git a/wasm2c/examples/callback/main.c b/wasm2c/examples/callback/main.c
new file mode 100644
index 00000000..b0b37d46
--- /dev/null
+++ b/wasm2c/examples/callback/main.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+
+#include "callback.h"
+
+/*
+ * The callback function. Prints the null-terminated string at the given
+ * location in the instance's exported memory.
+ */
+void print(Z_callback_instance_t* instance, uint32_t ptr) {
+ puts(Z_callbackZ_memory(instance)->data + ptr);
+}
+
+int main(int argc, char** argv) {
+ /* Initialize the Wasm runtime. */
+ wasm_rt_init();
+
+ /* Instantiate the callback module. */
+ Z_callback_instance_t inst;
+ Z_callback_instantiate(&inst);
+
+ /*
+ * Call the module's "set_print_function" function, which takes a funcref to
+ * the callback. A funcref has three members: the function type (which can be
+ * looked up with "Z_callback_get_func_type"), a pointer to the function, and
+ * a module instance pointer that will be passed to the function when called.
+ */
+ wasm_rt_func_type_t fn_type = Z_callback_get_func_type(1, 0, WASM_RT_I32);
+ wasm_rt_funcref_t fn_ref = {fn_type, (wasm_rt_function_ptr_t)print, &inst};
+ Z_callbackZ_set_print_function(&inst, fn_ref);
+
+ /* "say_hello" uses the previously installed callback. */
+ Z_callbackZ_say_hello(&inst);
+
+ /* Free the module instance and the Wasm runtime state. */
+ Z_callback_free(&inst);
+ wasm_rt_free();
+
+ return 0;
+}
diff --git a/wasm2c/examples/fac/fac.c b/wasm2c/examples/fac/fac.c
index fd2fd3b9..bdc9e63f 100644
--- a/wasm2c/examples/fac/fac.c
+++ b/wasm2c/examples/fac/fac.c
@@ -1,6 +1,7 @@
/* Automatically generated by wasm2c */
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#if defined(_MSC_VER)
@@ -29,10 +30,15 @@
#define UNREACHABLE TRAP(UNREACHABLE)
-#define CALL_INDIRECT(table, t, ft, x, ...) \
- (LIKELY((x) < table.size && table.data[x].func && \
- table.data[x].func_type == func_types[ft]) || \
- TRAP(CALL_INDIRECT), \
+static inline bool func_types_eq(const wasm_rt_func_type_t a,
+ const wasm_rt_func_type_t b) {
+ return (a == b) || LIKELY(a && b && !memcmp(a, b, 32));
+}
+
+#define CALL_INDIRECT(table, t, ft, x, ...) \
+ (LIKELY((x) < table.size && table.data[x].func && \
+ func_types_eq(ft, table.data[x].func_type)) || \
+ TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#ifdef SUPPORT_MEMORY64
@@ -494,7 +500,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
}
typedef struct {
- uint32_t func_type_index;
+ wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
size_t module_offset;
} wasm_elem_segment_expr_t;
@@ -505,17 +511,16 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
u32 dest_addr,
u32 src_addr,
u32 n,
- void* module_instance,
- const u32* func_types) {
+ void* module_instance) {
if (UNLIKELY(src_addr + (uint64_t)n > src_size))
TRAP(OOB);
if (UNLIKELY(dest_addr + (uint64_t)n > dest->size))
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
- dest->data[dest_addr + i] = (wasm_rt_funcref_t){
- func_types[src_expr->func_type_index], src_expr->func,
- (char*)module_instance + src_expr->module_offset};
+ dest->data[dest_addr + i] =
+ (wasm_rt_funcref_t){src_expr->type, src_expr->func,
+ (char*)module_instance + src_expr->module_offset};
}
}
@@ -586,13 +591,13 @@ DEFINE_TABLE_SET(externref)
DEFINE_TABLE_FILL(funcref)
DEFINE_TABLE_FILL(externref)
-static bool s_module_initialized = false;
-
-static u32 func_types[1];
+#if defined(__GNUC__) || defined(__clang__)
+#define FUNC_TYPE_T(x) static const char* const x
+#else
+#define FUNC_TYPE_T(x) static const char x[]
+#endif
-static void init_func_types(void) {
- func_types[0] = wasm_rt_register_func_type(1, 1, WASM_RT_I32, WASM_RT_I32);
-}
+FUNC_TYPE_T(w2c_t0) = "\x07\x80\x96\x7a\x42\xf7\x3e\xe6\x70\x5c\x2f\xac\x83\xf5\x67\xd2\xa2\xa0\x69\x41\x5f\xf8\xe7\x96\x7f\x23\xab\x00\x03\x5f\x4a\x3c";
static u32 w2c_fac(Z_fac_instance_t*, u32);
@@ -621,16 +626,24 @@ u32 Z_facZ_fac(Z_fac_instance_t* instance, u32 w2c_p0) {
return w2c_fac(instance, w2c_p0);
}
-void Z_fac_init_module(void) {
- assert(wasm_rt_is_initialized());
- s_module_initialized = true;
- init_func_types();
-}
-
void Z_fac_instantiate(Z_fac_instance_t* instance) {
assert(wasm_rt_is_initialized());
- assert(s_module_initialized);
}
void Z_fac_free(Z_fac_instance_t* instance) {
}
+
+wasm_rt_func_type_t Z_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...) {
+ va_list args;
+
+ if (param_count == 1 && result_count == 1) {
+ va_start(args, result_count);
+ if (true && va_arg(args, wasm_rt_type_t) == WASM_RT_I32 && va_arg(args, wasm_rt_type_t) == WASM_RT_I32) {
+ va_end(args);
+ return w2c_t0;
+ }
+ va_end(args);
+ }
+
+ return NULL;
+}
diff --git a/wasm2c/examples/fac/fac.h b/wasm2c/examples/fac/fac.h
index 6e36ada2..7700bc42 100644
--- a/wasm2c/examples/fac/fac.h
+++ b/wasm2c/examples/fac/fac.h
@@ -29,9 +29,9 @@ typedef struct Z_fac_instance_t {
char dummy_member;
} Z_fac_instance_t;
-void Z_fac_init_module(void);
void Z_fac_instantiate(Z_fac_instance_t*);
void Z_fac_free(Z_fac_instance_t*);
+wasm_rt_func_type_t Z_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...);
/* export: 'fac' */
u32 Z_facZ_fac(Z_fac_instance_t*, u32);
diff --git a/wasm2c/examples/fac/main.c b/wasm2c/examples/fac/main.c
index 30bf5d0a..37593f6e 100644
--- a/wasm2c/examples/fac/main.c
+++ b/wasm2c/examples/fac/main.c
@@ -17,10 +17,6 @@ int main(int argc, char** argv) {
/* Initialize the Wasm runtime. */
wasm_rt_init();
- /* Initialize the `fac` module (this registers the module's function types in
- * a global data structure) */
- Z_fac_init_module();
-
/* Declare an instance of the `fac` module. */
Z_fac_instance_t instance;
diff --git a/wasm2c/examples/rot13/main.c b/wasm2c/examples/rot13/main.c
index 7348d005..d6517f39 100644
--- a/wasm2c/examples/rot13/main.c
+++ b/wasm2c/examples/rot13/main.c
@@ -53,9 +53,6 @@ int main(int argc, char** argv) {
/* Initialize the Wasm runtime. */
wasm_rt_init();
- /* Initialize the rot13 module. */
- Z_rot13_init_module();
-
/* Declare an instance of the `rot13` module. */
Z_rot13_instance_t rot13_instance;
diff --git a/wasm2c/wasm-rt-impl.c b/wasm2c/wasm-rt-impl.c
index dbd7ce41..0430e5f3 100644
--- a/wasm2c/wasm-rt-impl.c
+++ b/wasm2c/wasm-rt-impl.c
@@ -45,13 +45,6 @@
#define PAGE_SIZE 65536
#define MAX_EXCEPTION_SIZE PAGE_SIZE
-typedef struct FuncType {
- wasm_rt_type_t* params;
- wasm_rt_type_t* results;
- uint32_t param_count;
- uint32_t result_count;
-} FuncType;
-
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
static bool g_signal_handler_installed = false;
#ifdef _WIN32
@@ -66,9 +59,6 @@ WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth;
WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth;
#endif
-static FuncType* g_func_types;
-static uint32_t g_func_type_count;
-
WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf;
static WASM_RT_THREAD_LOCAL wasm_rt_tag_t g_active_exception_tag;
@@ -91,59 +81,6 @@ void wasm_rt_trap(wasm_rt_trap_t code) {
#endif
}
-static bool func_types_are_equal(FuncType* a, FuncType* b) {
- if (a->param_count != b->param_count || a->result_count != b->result_count)
- return 0;
- uint32_t i;
- for (i = 0; i < a->param_count; ++i)
- if (a->params[i] != b->params[i])
- return 0;
- for (i = 0; i < a->result_count; ++i)
- if (a->results[i] != b->results[i])
- return 0;
- return 1;
-}
-
-uint32_t wasm_rt_register_func_type(uint32_t param_count,
- uint32_t result_count,
- ...) {
- size_t param_size = param_count * sizeof(wasm_rt_type_t);
- size_t result_size = result_count * sizeof(wasm_rt_type_t);
- FuncType func_type;
- func_type.param_count = param_count;
- func_type.params = alloca(param_size);
- func_type.result_count = result_count;
- func_type.results = alloca(result_size);
-
- va_list args;
- va_start(args, result_count);
-
- uint32_t i;
- for (i = 0; i < param_count; ++i)
- func_type.params[i] = va_arg(args, wasm_rt_type_t);
- for (i = 0; i < result_count; ++i)
- func_type.results[i] = va_arg(args, wasm_rt_type_t);
- va_end(args);
-
- for (i = 0; i < g_func_type_count; ++i)
- if (func_types_are_equal(&g_func_types[i], &func_type))
- return i + 1;
-
- // This is a new/unseed type. Copy our stack allocated params/results into
- // permanent heap allocated space.
- wasm_rt_type_t* params = malloc(param_size);
- wasm_rt_type_t* results = malloc(result_size);
- memcpy(params, func_type.params, param_size);
- memcpy(results, func_type.results, result_size);
- func_type.params = params;
- func_type.results = results;
-
- uint32_t idx = g_func_type_count++;
- g_func_types = realloc(g_func_types, g_func_type_count * sizeof(FuncType));
- g_func_types[idx] = func_type;
- return idx + 1;
-}
-
void wasm_rt_load_exception(const wasm_rt_tag_t tag,
uint32_t size,
const void* values) {
@@ -346,15 +283,6 @@ bool wasm_rt_is_initialized(void) {
}
void wasm_rt_free(void) {
- for (uint32_t i = 0; i < g_func_type_count; ++i) {
- free(g_func_types[i].params);
- free(g_func_types[i].results);
- }
-
- g_func_type_count = 0;
- free(g_func_types);
- g_func_types = NULL;
-
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
os_cleanup_signal_handler();
#endif
diff --git a/wasm2c/wasm-rt.h b/wasm2c/wasm-rt.h
index 64e6f972..871fb8ce 100644
--- a/wasm2c/wasm-rt.h
+++ b/wasm2c/wasm-rt.h
@@ -179,11 +179,17 @@ typedef enum {
*/
typedef void (*wasm_rt_function_ptr_t)(void);
+/**
+ * The type of a function (an arbitrary number of param and result types).
+ * This is represented as an opaque 256-bit ID.
+ */
+typedef const char* wasm_rt_func_type_t;
+
/** A function instance (the runtime representation of a function).
* These can be stored in tables of type funcref, or used as values. */
typedef struct {
- /** The index as returned from `wasm_rt_register_func_type`. */
- uint32_t func_type;
+ /** The function's type. */
+ wasm_rt_func_type_t func_type;
/** The function. The embedder must know the actual C signature of the
* function and cast to it before calling. */
wasm_rt_function_ptr_t func;
@@ -194,7 +200,7 @@ typedef struct {
} wasm_rt_funcref_t;
/** Default (null) value of a funcref */
-static const wasm_rt_funcref_t wasm_rt_funcref_null_value = {0, NULL, NULL};
+static const wasm_rt_funcref_t wasm_rt_funcref_null_value = {NULL, NULL, NULL};
/** The type of an external reference (opaque to WebAssembly). */
typedef void* wasm_rt_externref_t;
@@ -260,29 +266,7 @@ WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t);
const char* wasm_rt_strerror(wasm_rt_trap_t trap);
/**
- * Register a function type with the given signature. The returned function
- * index is guaranteed to be the same for all calls with the same signature.
- * The following varargs must all be of type `wasm_rt_type_t`, first the
- * params` and then the `results`.
- *
- * ```
- * // Register (func (param i32 f32) (result i64)).
- * wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
- * => returns 1
- *
- * // Register (func (result i64)).
- * wasm_rt_register_func_type(0, 1, WASM_RT_I32);
- * => returns 2
- *
- * // Register (func (param i32 f32) (result i64)) again.
- * wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
- * => returns 1
- * ```
- */
-uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...);
-
-/**
- * An tag is represented as an arbitrary pointer.
+ * A tag is represented as an arbitrary pointer.
*/
typedef const void* wasm_rt_tag_t;