diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 20 | ||||
-rw-r--r-- | include/wabt/sha256.h | 31 | ||||
-rw-r--r-- | src/c-writer.cc | 158 | ||||
-rw-r--r-- | src/config.h.in | 3 | ||||
-rw-r--r-- | src/sha256.cc | 48 | ||||
-rw-r--r-- | src/template/wasm2c.declarations.c | 30 | ||||
-rw-r--r-- | src/template/wasm2c.includes.c | 1 | ||||
-rwxr-xr-x | test/run-spec-wasm2c.py | 2 | ||||
-rw-r--r-- | test/spec-wasm2c-prefix.c | 9 | ||||
-rw-r--r-- | test/wasm2c/add.txt | 61 | ||||
-rw-r--r-- | test/wasm2c/hello.txt | 89 | ||||
-rw-r--r-- | test/wasm2c/minimal.txt | 45 | ||||
m--------- | third_party/picosha2 | 0 | ||||
-rw-r--r-- | wasm2c/.gitignore | 4 | ||||
-rw-r--r-- | wasm2c/README.md | 62 | ||||
-rw-r--r-- | wasm2c/examples/callback/Makefile | 19 | ||||
-rw-r--r-- | wasm2c/examples/callback/callback.wat | 19 | ||||
-rw-r--r-- | wasm2c/examples/callback/main.c | 39 | ||||
-rw-r--r-- | wasm2c/examples/fac/fac.c | 59 | ||||
-rw-r--r-- | wasm2c/examples/fac/fac.h | 2 | ||||
-rw-r--r-- | wasm2c/examples/fac/main.c | 4 | ||||
-rw-r--r-- | wasm2c/examples/rot13/main.c | 3 | ||||
-rw-r--r-- | wasm2c/wasm-rt-impl.c | 72 | ||||
-rw-r--r-- | wasm2c/wasm-rt.h | 36 |
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; |