summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--README.md1
-rw-r--r--src/c-writer.cc2437
-rw-r--r--src/c-writer.h37
-rw-r--r--src/ir.cc23
-rw-r--r--src/ir.h4
-rw-r--r--src/stream.cc7
-rw-r--r--src/stream.h4
-rw-r--r--src/tools/wasm2c.cc149
-rw-r--r--test/README.md6
-rw-r--r--test/find_exe.py6
-rwxr-xr-xtest/run-spec-wasm2c.py398
-rwxr-xr-xtest/run-tests.py14
-rw-r--r--test/spec-wasm2c-prefix.c323
-rw-r--r--test/utils.py18
-rw-r--r--test/wasm2c/spec/address.txt5
-rw-r--r--test/wasm2c/spec/align.txt5
-rw-r--r--test/wasm2c/spec/binary.txt5
-rw-r--r--test/wasm2c/spec/block.txt5
-rw-r--r--test/wasm2c/spec/br.txt5
-rw-r--r--test/wasm2c/spec/br_if.txt5
-rw-r--r--test/wasm2c/spec/br_table.txt6
-rw-r--r--test/wasm2c/spec/break-drop.txt5
-rw-r--r--test/wasm2c/spec/call.txt5
-rw-r--r--test/wasm2c/spec/call_indirect.txt5
-rw-r--r--test/wasm2c/spec/comments.txt5
-rw-r--r--test/wasm2c/spec/const.txt5
-rw-r--r--test/wasm2c/spec/conversions.txt5
-rw-r--r--test/wasm2c/spec/custom_section.txt5
-rw-r--r--test/wasm2c/spec/endianness.txt5
-rw-r--r--test/wasm2c/spec/exports.txt6
-rw-r--r--test/wasm2c/spec/f32.txt6
-rw-r--r--test/wasm2c/spec/f32_bitwise.txt5
-rw-r--r--test/wasm2c/spec/f32_cmp.txt6
-rw-r--r--test/wasm2c/spec/f64.txt6
-rw-r--r--test/wasm2c/spec/f64_bitwise.txt5
-rw-r--r--test/wasm2c/spec/f64_cmp.txt6
-rw-r--r--test/wasm2c/spec/fac.txt5
-rw-r--r--test/wasm2c/spec/float_exprs.txt6
-rw-r--r--test/wasm2c/spec/float_literals.txt5
-rw-r--r--test/wasm2c/spec/float_memory.txt5
-rw-r--r--test/wasm2c/spec/float_misc.txt5
-rw-r--r--test/wasm2c/spec/forward.txt5
-rw-r--r--test/wasm2c/spec/func.txt5
-rw-r--r--test/wasm2c/spec/func_ptrs.txt6
-rw-r--r--test/wasm2c/spec/get_local.txt5
-rw-r--r--test/wasm2c/spec/globals.txt5
-rw-r--r--test/wasm2c/spec/i32.txt5
-rw-r--r--test/wasm2c/spec/i64.txt5
-rw-r--r--test/wasm2c/spec/if.txt5
-rw-r--r--test/wasm2c/spec/imports.txt16
-rw-r--r--test/wasm2c/spec/inline-module.txt5
-rw-r--r--test/wasm2c/spec/int_exprs.txt5
-rw-r--r--test/wasm2c/spec/int_literals.txt5
-rw-r--r--test/wasm2c/spec/labels.txt5
-rw-r--r--test/wasm2c/spec/left-to-right.txt5
-rw-r--r--test/wasm2c/spec/linking.txt5
-rw-r--r--test/wasm2c/spec/loop.txt5
-rw-r--r--test/wasm2c/spec/memory.txt5
-rw-r--r--test/wasm2c/spec/memory_redundancy.txt5
-rw-r--r--test/wasm2c/spec/memory_trap.txt5
-rw-r--r--test/wasm2c/spec/names.txt7
-rw-r--r--test/wasm2c/spec/nop.txt5
-rw-r--r--test/wasm2c/spec/resizing.txt5
-rw-r--r--test/wasm2c/spec/return.txt5
-rw-r--r--test/wasm2c/spec/select.txt5
-rw-r--r--test/wasm2c/spec/set_local.txt5
-rw-r--r--test/wasm2c/spec/skip-stack-guard-page.txt5
-rw-r--r--test/wasm2c/spec/stack.txt5
-rw-r--r--test/wasm2c/spec/start.txt8
-rw-r--r--test/wasm2c/spec/store_retval.txt5
-rw-r--r--test/wasm2c/spec/switch.txt5
-rw-r--r--test/wasm2c/spec/tee_local.txt5
-rw-r--r--test/wasm2c/spec/token.txt5
-rw-r--r--test/wasm2c/spec/traps.txt5
-rw-r--r--test/wasm2c/spec/type.txt5
-rw-r--r--test/wasm2c/spec/typecheck.txt5
-rw-r--r--test/wasm2c/spec/unreachable.txt5
-rw-r--r--test/wasm2c/spec/unreached-invalid.txt5
-rw-r--r--test/wasm2c/spec/unwind.txt5
-rw-r--r--test/wasm2c/spec/utf8-custom-section-id.txt5
-rw-r--r--test/wasm2c/spec/utf8-import-field.txt5
-rw-r--r--test/wasm2c/spec/utf8-import-module.txt5
-rw-r--r--test/wasm2c/spec/utf8-invalid-encoding.txt5
84 files changed, 3793 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb4de623..91769128 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -297,6 +297,10 @@ if (NOT EMSCRIPTEN)
# wasm2wat
wabt_executable(wasm2wat src/tools/wasm2wat.cc)
+ # wasm2c
+ wabt_executable(wasm2c
+ src/tools/wasm2c.cc src/c-writer.cc)
+
# wasm-opcodecnt
wabt_executable(wasm-opcodecnt
src/tools/wasm-opcodecnt.cc src/binary-reader-opcnt.cc)
diff --git a/README.md b/README.md
index da85e01c..e978419e 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ WABT (we pronounce it "wabbit") is a suite of tools for WebAssembly, including:
- **wasm-interp**: decode and run a WebAssembly binary file using a stack-based interpreter
- **wat-desugar**: parse .wat text form as supported by the spec interpreter (s-expressions, flat syntax, or mixed) and print "canonical" flat format
- **wasm-link**: simple linker for merging multiple wasm files.
+ - **wasm2c**: convert a WebAssembly binary file to a C source and header
These tools are intended for use in (or for development of) toolchains or other
systems that want to manipulate WebAssembly files. Unlike the WebAssembly spec
diff --git a/src/c-writer.cc b/src/c-writer.cc
new file mode 100644
index 00000000..64b6f539
--- /dev/null
+++ b/src/c-writer.cc
@@ -0,0 +1,2437 @@
+/*
+ * 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 "src/c-writer.h"
+
+#include <cctype>
+#include <cinttypes>
+#include <map>
+#include <set>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/ir.h"
+#include "src/literal.h"
+#include "src/stream.h"
+#include "src/string-view.h"
+
+#define INDENT_SIZE 2
+
+#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()
+
+namespace wabt {
+
+namespace {
+
+struct Label {
+ Label(LabelType label_type,
+ const std::string& name,
+ const BlockSignature& sig,
+ size_t type_stack_size,
+ bool used = false)
+ : label_type(label_type),
+ name(name),
+ sig(sig),
+ type_stack_size(type_stack_size),
+ used(used) {}
+
+ bool HasValue() const {
+ return label_type != LabelType::Loop && !sig.empty();
+ }
+
+ LabelType label_type;
+ const std::string& name;
+ const BlockSignature& sig;
+ size_t type_stack_size;
+ bool used = false;
+};
+
+template <int>
+struct Name {
+ explicit Name(const std::string& name) : name(name) {}
+ const std::string& name;
+};
+
+typedef Name<0> LocalName;
+typedef Name<1> GlobalName;
+typedef Name<2> ExternalPtr;
+typedef Name<3> ExternalRef;
+
+struct GotoLabel {
+ explicit GotoLabel(const Var& var) : var(var) {}
+ const Var& var;
+};
+
+struct LabelDecl {
+ explicit LabelDecl(const std::string& name) : name(name) {}
+ std::string name;
+};
+
+struct GlobalVar {
+ explicit GlobalVar(const Var& var) : var(var) {}
+ const Var& var;
+};
+
+struct StackVar {
+ explicit StackVar(Index index, Type type = Type::Any)
+ : index(index), type(type) {}
+ Index index;
+ Type type;
+};
+
+struct TypeEnum {
+ explicit TypeEnum(Type type) : type(type) {}
+ Type type;
+};
+
+struct SignedType {
+ explicit SignedType(Type type) : type(type) {}
+ Type type;
+};
+
+struct ResultType {
+ explicit ResultType(const TypeVector& types) : types(types) {}
+ const TypeVector& types;
+};
+
+struct Newline {};
+struct OpenBrace {};
+struct CloseBrace {};
+
+int GetShiftMask(Type type) {
+ switch (type) {
+ case Type::I32: return 31;
+ case Type::I64: return 63;
+ default: WABT_UNREACHABLE; return 0;
+ }
+}
+
+class CWriter {
+ public:
+ CWriter(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const WriteCOptions* options)
+ : options_(options),
+ c_stream_(c_stream),
+ h_stream_(h_stream),
+ header_name_(header_name) {}
+
+ Result WriteModule(const Module&);
+
+ private:
+ typedef std::set<std::string> SymbolSet;
+ typedef std::map<std::string, std::string> SymbolMap;
+ typedef std::pair<Index, Type> StackTypePair;
+ typedef std::map<StackTypePair, std::string> StackVarSymbolMap;
+
+ void UseStream(Stream*);
+
+ void WriteCHeader();
+ void WriteCSource();
+
+ size_t MarkTypeStack() const;
+ void ResetTypeStack(size_t mark);
+ Type StackType(Index) const;
+ void PushType(Type);
+ void PushTypes(const TypeVector&);
+ void DropTypes(size_t count);
+
+ void PushLabel(LabelType,
+ const std::string& name,
+ const BlockSignature&,
+ bool used = false);
+ const Label* FindLabel(const Var& var);
+ bool IsTopLabelUsed() const;
+ void PopLabel();
+
+ static std::string AddressOf(const std::string&);
+ static std::string Deref(const std::string&);
+
+ static char MangleType(Type);
+ static std::string MangleTypes(const TypeVector&);
+ static std::string MangleName(string_view);
+ static std::string MangleFuncName(string_view,
+ const TypeVector& param_types,
+ const TypeVector& result_types);
+ static std::string MangleGlobalName(string_view, Type);
+ static std::string LegalizeName(string_view);
+ static std::string ExportName(string_view mangled_name);
+ std::string DefineName(SymbolSet*, string_view);
+ std::string DefineImportName(const std::string& name,
+ string_view module_name,
+ string_view mangled_field_name);
+ std::string DefineGlobalScopeName(const std::string&);
+ std::string DefineLocalScopeName(const std::string&);
+ std::string DefineStackVarName(Index, Type, string_view);
+
+ void Indent(int size = INDENT_SIZE);
+ void Dedent(int size = INDENT_SIZE);
+ void WriteIndent();
+ void WriteData(const void* src, size_t size);
+ void Writef(const char* format, ...);
+
+ template <typename T, typename U, typename... Args>
+ void Write(T&& t, U&& u, Args&&... args) {
+ Write(std::forward<T>(t));
+ Write(std::forward<U>(u));
+ Write(std::forward<Args>(args)...);
+ }
+
+ std::string GetGlobalName(const std::string&) const;
+
+ enum class WriteExportsKind {
+ Declarations,
+ Definitions,
+ Initializers,
+ };
+
+ void Write() {}
+ void Write(Newline);
+ void Write(OpenBrace);
+ void Write(CloseBrace);
+ void Write(Index);
+ void Write(string_view);
+ void Write(const LocalName&);
+ void Write(const GlobalName&);
+ void Write(const ExternalPtr&);
+ void Write(const ExternalRef&);
+ void Write(Type);
+ void Write(SignedType);
+ void Write(TypeEnum);
+ void Write(const Var&);
+ void Write(const GotoLabel&);
+ void Write(const LabelDecl&);
+ void Write(const GlobalVar&);
+ void Write(const StackVar&);
+ void Write(const ResultType&);
+ void Write(const Const&);
+ void WriteInitExpr(const ExprList&);
+ void InitGlobalSymbols();
+ std::string GenerateHeaderGuard() const;
+ void WriteSourceTop();
+ void WriteFuncTypes();
+ void WriteImports();
+ void WriteFuncDeclarations();
+ void WriteFuncDeclaration(const FuncDeclaration&, const std::string&);
+ void WriteGlobals();
+ void WriteGlobal(const Global&, const std::string&);
+ void WriteMemories();
+ void WriteMemory(const std::string&);
+ void WriteTables();
+ void WriteTable(const std::string&);
+ void WriteDataInitializers();
+ void WriteElemInitializers();
+ void WriteInitExports();
+ void WriteExports(WriteExportsKind);
+ void WriteInit();
+ void WriteFuncs();
+ void Write(const Func&);
+ void WriteParams();
+ void WriteLocals();
+ void WriteStackVarDeclarations();
+ void Write(const ExprList&);
+
+ enum class AssignOp {
+ Disallowed,
+ Allowed,
+ };
+
+ void WriteSimpleUnaryExpr(Opcode, const char* op);
+ void WriteInfixBinaryExpr(Opcode,
+ const char* op,
+ AssignOp = AssignOp::Allowed);
+ void WritePrefixBinaryExpr(Opcode, const char* op);
+ void WriteSignedBinaryExpr(Opcode, const char* op);
+ void Write(const BinaryExpr&);
+ void Write(const CompareExpr&);
+ void Write(const ConvertExpr&);
+ void Write(const LoadExpr&);
+ void Write(const StoreExpr&);
+ void Write(const UnaryExpr&);
+
+ const WriteCOptions* options_ = nullptr;
+ const Module* module_ = nullptr;
+ const Func* func_ = nullptr;
+ Stream* stream_ = nullptr;
+ MemoryStream func_stream_;
+ Stream* c_stream_ = nullptr;
+ Stream* h_stream_ = nullptr;
+ std::string header_name_;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ bool should_write_indent_next_ = false;
+
+ SymbolMap global_sym_map_;
+ SymbolMap local_sym_map_;
+ StackVarSymbolMap stack_var_sym_map_;
+ SymbolSet global_syms_;
+ SymbolSet local_syms_;
+ SymbolSet import_syms_;
+ TypeVector type_stack_;
+ std::vector<Label> label_stack_;
+};
+
+static const char kImplicitFuncLabel[] = "$Bfunc";
+
+static const char* s_global_symbols[] = {
+ // keywords
+ "_Alignas", "_Alignof", "asm", "_Atomic", "auto", "_Bool", "break", "case",
+ "char", "_Complex", "const", "continue", "default", "do", "double", "else",
+ "enum", "extern", "float", "for", "_Generic", "goto", "if", "_Imaginary",
+ "inline", "int", "long", "_Noreturn", "_Pragma", "register", "restrict",
+ "return", "short", "signed", "sizeof", "static", "_Static_assert", "struct",
+ "switch", "_Thread_local", "typedef", "union", "unsigned", "void",
+ "volatile", "while",
+
+ // assert.h
+ "assert", "static_assert",
+
+ // math.h
+ "abs", "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh", "cbrt",
+ "ceil", "copysign", "cos", "cosh", "double_t", "erf", "erfc", "exp", "exp2",
+ "expm1", "fabs", "fdim", "float_t", "floor", "fma", "fmax", "fmin", "fmod",
+ "fpclassify", "FP_FAST_FMA", "FP_FAST_FMAF", "FP_FAST_FMAL", "FP_ILOGB0",
+ "FP_ILOGBNAN", "FP_INFINITE", "FP_NAN", "FP_NORMAL", "FP_SUBNORMAL",
+ "FP_ZERO", "frexp", "HUGE_VAL", "HUGE_VALF", "HUGE_VALL", "hypot", "ilogb",
+ "INFINITY", "isfinite", "isgreater", "isgreaterequal", "isinf", "isless",
+ "islessequal", "islessgreater", "isnan", "isnormal", "isunordered", "ldexp",
+ "lgamma", "llrint", "llround", "log", "log10", "log1p", "log2", "logb",
+ "lrint", "lround", "MATH_ERREXCEPT", "math_errhandling", "MATH_ERRNO",
+ "modf", "nan", "NAN", "nanf", "nanl", "nearbyint", "nextafter",
+ "nexttoward", "pow", "remainder", "remquo", "rint", "round", "scalbln",
+ "scalbn", "signbit", "sin", "sinh", "sqrt", "tan", "tanh", "tgamma",
+ "trunc",
+
+ // stdint.h
+ "INT16_C", "INT16_MAX", "INT16_MIN", "int16_t", "INT32_MAX", "INT32_MIN",
+ "int32_t", "INT64_C", "INT64_MAX", "INT64_MIN", "int64_t", "INT8_C",
+ "INT8_MAX", "INT8_MIN", "int8_t", "INT_FAST16_MAX", "INT_FAST16_MIN",
+ "int_fast16_t", "INT_FAST32_MAX", "INT_FAST32_MIN", "int_fast32_t",
+ "INT_FAST64_MAX", "INT_FAST64_MIN", "int_fast64_t", "INT_FAST8_MAX",
+ "INT_FAST8_MIN", "int_fast8_t", "INT_LEAST16_MAX", "INT_LEAST16_MIN",
+ "int_least16_t", "INT_LEAST32_MAX", "INT_LEAST32_MIN", "int_least32_t",
+ "INT_LEAST64_MAX", "INT_LEAST64_MIN", "int_least64_t", "INT_LEAST8_MAX",
+ "INT_LEAST8_MIN", "int_least8_t", "INTMAX_C", "INTMAX_MAX", "INTMAX_MIN",
+ "intmax_t", "INTPTR_MAX", "INTPTR_MIN", "intptr_t", "PTRDIFF_MAX",
+ "PTRDIFF_MIN", "SIG_ATOMIC_MAX", "SIG_ATOMIC_MIN", "SIZE_MAX", "UINT16_C",
+ "UINT16_MAX", "uint16_t", "UINT32_C", "UINT32_MAX", "uint32_t", "UINT64_C",
+ "UINT64_MAX", "uint64_t", "UINT8_MAX", "uint8_t", "UINT_FAST16_MAX",
+ "uint_fast16_t", "UINT_FAST32_MAX", "uint_fast32_t", "UINT_FAST64_MAX",
+ "uint_fast64_t", "UINT_FAST8_MAX", "uint_fast8_t", "UINT_LEAST16_MAX",
+ "uint_least16_t", "UINT_LEAST32_MAX", "uint_least32_t", "UINT_LEAST64_MAX",
+ "uint_least64_t", "UINT_LEAST8_MAX", "uint_least8_t", "UINTMAX_C",
+ "UINTMAX_MAX", "uintmax_t", "UINTPTR_MAX", "uintptr_t", "UNT8_C",
+ "WCHAR_MAX", "WCHAR_MIN", "WINT_MAX", "WINT_MIN",
+
+ // stdlib.h
+ "abort", "abs", "atexit", "atof", "atoi", "atol", "atoll", "at_quick_exit",
+ "bsearch", "calloc", "div", "div_t", "exit", "_Exit", "EXIT_FAILURE",
+ "EXIT_SUCCESS", "free", "getenv", "labs", "ldiv", "ldiv_t", "llabs",
+ "lldiv", "lldiv_t", "malloc", "MB_CUR_MAX", "mblen", "mbstowcs", "mbtowc",
+ "qsort", "quick_exit", "rand", "RAND_MAX", "realloc", "size_t", "srand",
+ "strtod", "strtof", "strtol", "strtold", "strtoll", "strtoul", "strtoull",
+ "system", "wcstombs", "wctomb",
+
+ // string.h
+ "memchr", "memcmp", "memcpy", "memmove", "memset", "NULL", "size_t",
+ "strcat", "strchr", "strcmp", "strcoll", "strcpy", "strcspn", "strerror",
+ "strlen", "strncat", "strncmp", "strncpy", "strpbrk", "strrchr", "strspn",
+ "strstr", "strtok", "strxfrm",
+
+ // defined
+ "CALL_INDIRECT", "DEFINE_LOAD", "DEFINE_REINTERPRET", "DEFINE_STORE",
+ "DIVREM_U", "DIV_S", "DIV_U", "f32", "f32_load", "f32_reinterpret_i32",
+ "f32_store", "f64", "f64_load", "f64_reinterpret_i64", "f64_store", "FMAX",
+ "FMIN", "FUNC_EPILOGUE", "FUNC_PROLOGUE", "func_types", "I32_CLZ",
+ "I32_CLZ", "I32_DIV_S", "i32_load", "i32_load16_s", "i32_load16_u",
+ "i32_load8_s", "i32_load8_u", "I32_POPCNT", "i32_reinterpret_f32",
+ "I32_REM_S", "I32_ROTL", "I32_ROTR", "i32_store", "i32_store16",
+ "i32_store8", "I32_TRUNC_S_F32", "I32_TRUNC_S_F64", "I32_TRUNC_U_F32",
+ "I32_TRUNC_U_F64", "I64_CTZ", "I64_CTZ", "I64_DIV_S", "i64_load",
+ "i64_load16_s", "i64_load16_u", "i64_load32_s", "i64_load32_u",
+ "i64_load8_s", "i64_load8_u", "I64_POPCNT", "i64_reinterpret_f64",
+ "I64_REM_S", "I64_ROTL", "I64_ROTR", "i64_store", "i64_store16",
+ "i64_store32", "i64_store8", "I64_TRUNC_S_F32", "I64_TRUNC_S_F64",
+ "I64_TRUNC_U_F32", "I64_TRUNC_U_F64", "init", "init_elem_segment",
+ "init_func_types", "init_globals", "init_memory", "init_table", "LIKELY",
+ "MEMCHECK", "REM_S", "REM_U", "ROTL", "ROTR", "s16", "s32", "s64", "s8",
+ "TRAP", "TRUNC_S", "TRUNC_U", "Type", "u16", "u32", "u64", "u8", "UNLIKELY",
+ "UNREACHABLE", "WASM_RT_ADD_PREFIX", "wasm_rt_allocate_memory",
+ "wasm_rt_allocate_table", "wasm_rt_anyfunc_t", "wasm_rt_call_stack_depth",
+ "wasm_rt_elem_t", "WASM_RT_F32", "WASM_RT_F64", "wasm_rt_grow_memory",
+ "WASM_RT_I32", "WASM_RT_I64", "WASM_RT_INCLUDED_",
+ "WASM_RT_MAX_CALL_STACK_DEPTH", "wasm_rt_memory_t", "WASM_RT_MODULE_PREFIX",
+ "WASM_RT_PASTE_", "WASM_RT_PASTE", "wasm_rt_register_func_type",
+ "wasm_rt_table_t", "wasm_rt_trap", "WASM_RT_TRAP_CALL_INDIRECT",
+ "WASM_RT_TRAP_DIV_BY_ZERO", "WASM_RT_TRAP_EXHAUSTION",
+ "WASM_RT_TRAP_INT_OVERFLOW", "WASM_RT_TRAP_INVALID_CONVERSION",
+ "WASM_RT_TRAP_NONE", "WASM_RT_TRAP_OOB", "wasm_rt_trap_t",
+ "WASM_RT_TRAP_UNREACHABLE",
+
+};
+
+static const char s_header_top[] =
+ R"(
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WASM_RT_INCLUDED_
+#define WASM_RT_INCLUDED_
+
+#include <stdint.h>
+
+#ifndef WASM_RT_MAX_CALL_STACK_DEPTH
+#define WASM_RT_MAX_CALL_STACK_DEPTH 500
+#endif
+
+#ifndef WASM_RT_MODULE_PREFIX
+#define WASM_RT_MODULE_PREFIX
+#endif
+
+#define WASM_RT_PASTE_(x, y) x ## y
+#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)
+#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)
+
+#define WASM_RT_DEFINE_EXTERNAL(decl, target) decl = &target;
+
+/* TODO(binji): only use stdint.h types in header */
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef float f32;
+typedef double f64;
+
+typedef enum {
+ WASM_RT_TRAP_NONE,
+ WASM_RT_TRAP_OOB,
+ WASM_RT_TRAP_INT_OVERFLOW,
+ WASM_RT_TRAP_DIV_BY_ZERO,
+ WASM_RT_TRAP_INVALID_CONVERSION,
+ WASM_RT_TRAP_UNREACHABLE,
+ WASM_RT_TRAP_CALL_INDIRECT,
+ WASM_RT_TRAP_EXHAUSTION,
+} wasm_rt_trap_t;
+
+typedef enum {
+ WASM_RT_I32,
+ WASM_RT_I64,
+ WASM_RT_F32,
+ WASM_RT_F64,
+} wasm_rt_type_t;
+
+typedef void (*wasm_rt_anyfunc_t)(void);
+
+typedef struct {
+ uint32_t func_type;
+ wasm_rt_anyfunc_t func;
+} wasm_rt_elem_t;
+
+typedef struct {
+ uint8_t* data;
+ uint32_t pages, max_pages;
+ uint32_t size;
+} wasm_rt_memory_t;
+
+typedef struct {
+ wasm_rt_elem_t* data;
+ uint32_t max_size;
+ uint32_t size;
+} wasm_rt_table_t;
+
+extern void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn));
+extern uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...);
+extern void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages);
+extern uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages);
+extern void wasm_rt_allocate_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements);
+extern uint32_t wasm_rt_call_stack_depth;
+
+#endif /* WASM_RT_INCLUDED_ */
+
+extern void WASM_RT_ADD_PREFIX(init)(void);
+)";
+
+static const char s_header_bottom[] = R"(
+#ifdef __cplusplus
+}
+#endif
+)";
+
+static const char s_source_includes[] = R"(#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+)";
+
+static const char s_source_declarations[] = R"(
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+
+#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)
+
+#define FUNC_PROLOGUE \
+ if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \
+ TRAP(EXHAUSTION)
+
+#define FUNC_EPILOGUE --wasm_rt_call_stack_depth
+
+#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]) \
+ ? ((t)table.data[x].func)(__VA_ARGS__) \
+ : TRAP(CALL_INDIRECT))
+
+#define MEMCHECK(mem, a, t) \
+ if (UNLIKELY((a) + sizeof(t) > mem->size)) TRAP(OOB)
+
+#define DEFINE_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ memcpy(&result, &mem->data[addr], sizeof(t1)); \
+ return (t3)(t2)result; \
+ }
+
+#define DEFINE_STORE(name, t1, t2) \
+ static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 wrapped = (t1)value; \
+ memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \
+ }
+
+DEFINE_LOAD(i32_load, u32, u32, u32);
+DEFINE_LOAD(i64_load, u64, u64, u64);
+DEFINE_LOAD(f32_load, f32, f32, f32);
+DEFINE_LOAD(f64_load, f64, f64, f64);
+DEFINE_LOAD(i32_load8_s, s8, s32, u32);
+DEFINE_LOAD(i64_load8_s, s8, s64, u64);
+DEFINE_LOAD(i32_load8_u, u8, u32, u32);
+DEFINE_LOAD(i64_load8_u, u8, u64, u64);
+DEFINE_LOAD(i32_load16_s, s16, s32, u32);
+DEFINE_LOAD(i64_load16_s, s16, s64, u64);
+DEFINE_LOAD(i32_load16_u, u16, u32, u32);
+DEFINE_LOAD(i64_load16_u, u16, u64, u64);
+DEFINE_LOAD(i64_load32_s, s32, s64, u64);
+DEFINE_LOAD(i64_load32_u, u32, u64, u64);
+DEFINE_STORE(i32_store, u32, u32);
+DEFINE_STORE(i64_store, u64, u64);
+DEFINE_STORE(f32_store, f32, f32);
+DEFINE_STORE(f64_store, f64, f64);
+DEFINE_STORE(i32_store8, u8, u32);
+DEFINE_STORE(i32_store16, u16, u32);
+DEFINE_STORE(i64_store8, u8, u64);
+DEFINE_STORE(i64_store16, u16, u64);
+DEFINE_STORE(i64_store32, u32, u64);
+
+#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)
+#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)
+#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)
+#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)
+#define I32_POPCNT(x) (__builtin_popcount(x))
+#define I64_POPCNT(x) (__builtin_popcountll(x))
+
+#define DIV_S(ut, min, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
+ : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \
+ : (ut)((x) / (y)))
+
+#define REM_S(ut, min, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
+ : (UNLIKELY((x) == min && (y) == -1)) ? 0 \
+ : (ut)((x) % (y)))
+
+#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)
+#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)
+#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)
+#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)
+
+#define DIVREM_U(op, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))
+
+#define DIV_U(x, y) DIVREM_U(/, x, y)
+#define REM_U(x, y) DIVREM_U(%, x, y)
+
+#define ROTL(x, y, mask) \
+ (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))
+#define ROTR(x, y, mask) \
+ (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))
+
+#define I32_ROTL(x, y) ROTL(x, y, 31)
+#define I64_ROTL(x, y) ROTL(x, y, 63)
+#define I32_ROTR(x, y) ROTR(x, y, 31)
+#define I64_ROTR(x, y) ROTR(x, y, 63)
+
+#define FMIN(x, y) \
+ ((UNLIKELY((x) != (x))) ? NAN \
+ : (UNLIKELY((y) != (y))) ? NAN \
+ : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \
+ : (x < y) ? x : y)
+
+#define FMAX(x, y) \
+ ((UNLIKELY((x) != (x))) ? NAN \
+ : (UNLIKELY((y) != (y))) ? NAN \
+ : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \
+ : (x > y) ? x : y)
+
+#define TRUNC_S(ut, st, ft, min, max, maxop, x) \
+ ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
+ : (UNLIKELY((x) < (ft)(min) || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \
+ : (ut)(st)(x))
+
+#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, INT32_MIN, INT32_MAX, >=, x)
+#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, INT64_MIN, INT64_MAX, >=, x)
+#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, INT32_MIN, INT32_MAX, >, x)
+#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, INT64_MIN, INT64_MAX, >=, x)
+
+#define TRUNC_U(ut, ft, max, maxop, x) \
+ ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
+ : (UNLIKELY((x) <= (ft)-1 || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \
+ : (ut)(x))
+
+#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, UINT32_MAX, >=, x)
+#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, UINT64_MAX, >=, x)
+#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, UINT32_MAX, >, x)
+#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, UINT64_MAX, >=, x)
+
+#define DEFINE_REINTERPRET(name, t1, t2) \
+ static inline t2 name(t1 x) { \
+ t2 result; \
+ memcpy(&result, &x, sizeof(result)); \
+ return result; \
+ }
+
+DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)
+DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)
+DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)
+DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)
+)";
+
+size_t CWriter::MarkTypeStack() const {
+ return type_stack_.size();
+}
+
+void CWriter::ResetTypeStack(size_t mark) {
+ assert(mark <= type_stack_.size());
+ type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
+}
+
+Type CWriter::StackType(Index index) const {
+ assert(index < type_stack_.size());
+ return *(type_stack_.rbegin() + index);
+}
+
+void CWriter::PushType(Type type) {
+ type_stack_.push_back(type);
+}
+
+void CWriter::PushTypes(const TypeVector& types) {
+ type_stack_.insert(type_stack_.end(), types.begin(), types.end());
+}
+
+void CWriter::DropTypes(size_t count) {
+ assert(count <= type_stack_.size());
+ type_stack_.erase(type_stack_.end() - count, type_stack_.end());
+}
+
+void CWriter::PushLabel(LabelType label_type,
+ const std::string& name,
+ const BlockSignature& sig,
+ bool used) {
+ label_stack_.emplace_back(label_type, name, sig, type_stack_.size(), used);
+}
+
+const Label* CWriter::FindLabel(const Var& var) {
+ Label* label = nullptr;
+
+ if (var.is_index()) {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ assert(var.index() + 1 == label_stack_.size());
+ label = &label_stack_[0];
+ } else {
+ assert(var.is_name());
+ for (Index i = label_stack_.size(); i > 0; --i) {
+ label = &label_stack_[i - 1];
+ if (label->name == var.name())
+ break;
+ }
+ }
+
+ assert(label);
+ label->used = true;
+ return label;
+}
+
+bool CWriter::IsTopLabelUsed() const {
+ assert(!label_stack_.empty());
+ return label_stack_.back().used;
+}
+
+void CWriter::PopLabel() {
+ label_stack_.pop_back();
+}
+
+// static
+std::string CWriter::AddressOf(const std::string& s) {
+ return "(&" + s + ")";
+}
+
+// static
+std::string CWriter::Deref(const std::string& s) {
+ return "(*" + s + ")";
+}
+
+// static
+char CWriter::MangleType(Type type) {
+ switch (type) {
+ case Type::I32: return 'i';
+ case Type::I64: return 'j';
+ case Type::F32: return 'f';
+ case Type::F64: return 'd';
+ default: WABT_UNREACHABLE;
+ }
+}
+
+// static
+std::string CWriter::MangleTypes(const TypeVector& types) {
+ if (types.empty())
+ return std::string("v");
+
+ std::string result;
+ for (auto type: types)
+ result += MangleType(type);
+ return result;
+}
+
+// static
+std::string CWriter::MangleName(string_view name) {
+ const char kPrefix = 'Z';
+ std::string result = "Z_";
+
+ if (!name.empty()) {
+ for (char c: name) {
+ if ((isalnum(c) && c != kPrefix) || c == '_') {
+ result += c;
+ } else {
+ result += kPrefix;
+ result += StringPrintf("%02X", static_cast<uint8_t>(c));
+ }
+ }
+ }
+
+ return result;
+}
+
+// static
+std::string CWriter::MangleFuncName(string_view name,
+ const TypeVector& param_types,
+ const TypeVector& result_types) {
+ std::string sig = MangleTypes(result_types) + MangleTypes(param_types);
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::MangleGlobalName(string_view name, Type type) {
+ std::string sig(1, MangleType(type));
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::ExportName(string_view mangled_name) {
+ return "WASM_RT_ADD_PREFIX(" + mangled_name.to_string() + ")";
+}
+
+// static
+std::string CWriter::LegalizeName(string_view name) {
+ if (name.empty())
+ return "_";
+
+ std::string result;
+ result = isalpha(name[0]) ? name[0] : '_';
+ for (size_t i = 1; i < name.size(); ++i)
+ result += isalnum(name[i]) ? name[i] : '_';
+
+ return result;
+}
+
+std::string CWriter::DefineName(SymbolSet* set, string_view name) {
+ std::string legal = LegalizeName(name);
+ if (set->find(legal) != set->end()) {
+ std::string base = legal + "_";;
+ size_t count = 0;
+ do {
+ legal = base + std::to_string(count++);
+ } while (set->find(legal) != set->end());
+ }
+ set->insert(legal);
+ return legal;
+}
+
+string_view StripLeadingDollar(string_view name) {
+ if(!name.empty() && name[0] == '$') {
+ name.remove_prefix(1);
+ }
+ return name;
+}
+
+std::string CWriter::DefineImportName(const std::string& name,
+ string_view module,
+ string_view mangled_field_name) {
+ std::string mangled = MangleName(module) + mangled_field_name.to_string();
+ import_syms_.insert(name);
+ global_syms_.insert(mangled);
+ global_sym_map_.insert(SymbolMap::value_type(name, mangled));
+ return "(*" + mangled + ")";
+}
+
+std::string CWriter::DefineGlobalScopeName(const std::string& name) {
+ std::string unique = DefineName(&global_syms_, StripLeadingDollar(name));
+ global_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineLocalScopeName(const std::string& name) {
+ std::string unique = DefineName(&local_syms_, StripLeadingDollar(name));
+ local_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineStackVarName(Index index,
+ Type type,
+ string_view name) {
+ std::string unique = DefineName(&local_syms_, name);
+ StackTypePair stp = {index, type};
+ stack_var_sym_map_.insert(StackVarSymbolMap::value_type(stp, unique));
+ return unique;
+}
+
+void CWriter::Indent(int size) {
+ indent_ += size;
+}
+
+void CWriter::Dedent(int size) {
+ indent_ -= size;
+ assert(indent_ >= 0);
+}
+
+void CWriter::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t to_write = indent_;
+ while (to_write >= s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ to_write -= s_indent_len;
+ }
+ if (to_write > 0) {
+ stream_->WriteData(s_indent, to_write);
+ }
+}
+
+void CWriter::WriteData(const void* src, size_t size) {
+ if (should_write_indent_next_) {
+ WriteIndent();
+ should_write_indent_next_ = false;
+ }
+ stream_->WriteData(src, size);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ WriteData(buffer, length);
+}
+
+void CWriter::Write(Newline) {
+ Write("\n");
+ should_write_indent_next_ = true;
+}
+
+void CWriter::Write(OpenBrace) {
+ Write("{");
+ Indent();
+ Write(Newline());
+}
+
+void CWriter::Write(CloseBrace) {
+ Dedent();
+ Write("}");
+}
+
+void CWriter::Write(Index index) {
+ Writef("%" PRIindex, index);
+}
+
+void CWriter::Write(string_view s) {
+ WriteData(s.data(), s.size());
+}
+
+void CWriter::Write(const LocalName& name) {
+ assert(local_sym_map_.count(name.name) == 1);
+ Write(local_sym_map_[name.name]);
+}
+
+std::string CWriter::GetGlobalName(const std::string& name) const {
+ assert(global_sym_map_.count(name) == 1);
+ auto iter = global_sym_map_.find(name);
+ assert(iter != global_sym_map_.end());
+ return iter->second;
+}
+
+void CWriter::Write(const GlobalName& name) {
+ Write(GetGlobalName(name.name));
+}
+
+void CWriter::Write(const ExternalPtr& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write(GetGlobalName(name.name));
+ } else {
+ Write(AddressOf(GetGlobalName(name.name)));
+ }
+}
+
+void CWriter::Write(const ExternalRef& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write(Deref(GetGlobalName(name.name)));
+ } else {
+ Write(GetGlobalName(name.name));
+ }
+}
+
+void CWriter::Write(const Var& var) {
+ assert(var.is_name());
+ Write(LocalName(var.name()));
+}
+
+void CWriter::Write(const GotoLabel& goto_label) {
+ const Label* label = FindLabel(goto_label.var);
+ if (label->HasValue()) {
+ assert(label->sig.size() == 1);
+ assert(type_stack_.size() >= label->type_stack_size);
+ Index dst = type_stack_.size() - label->type_stack_size - 1;
+ if (dst != 0)
+ Write(StackVar(dst, label->sig[0]), " = ", StackVar(0), "; ");
+ }
+
+ if (goto_label.var.is_name()) {
+ Write("goto ", goto_label.var, ";");
+ } else {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ Write("goto ", Var(kImplicitFuncLabel), ";");
+ }
+}
+
+void CWriter::Write(const LabelDecl& label) {
+ if (IsTopLabelUsed())
+ Write(label.name, ":;", Newline());
+}
+
+void CWriter::Write(const GlobalVar& var) {
+ assert(var.var.is_name());
+ Write(ExternalRef(var.var.name()));
+}
+
+void CWriter::Write(const StackVar& sv) {
+ Index index = type_stack_.size() - 1 - sv.index;
+ Type type = sv.type;
+ if (type == Type::Any) {
+ assert(index < type_stack_.size());
+ type = type_stack_[index];
+ }
+
+ StackTypePair stp = {index, type};
+ auto iter = stack_var_sym_map_.find(stp);
+ if (iter == stack_var_sym_map_.end()) {
+ std::string name = MangleType(type) + std::to_string(index);
+ Write(DefineStackVarName(index, type, name));
+ } else {
+ Write(iter->second);
+ }
+}
+
+void CWriter::Write(Type type) {
+ switch (type) {
+ case Type::I32: Write("u32"); break;
+ case Type::I64: Write("u64"); break;
+ case Type::F32: Write("f32"); break;
+ case Type::F64: Write("f64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(TypeEnum type) {
+ switch (type.type) {
+ case Type::I32: Write("WASM_RT_I32"); break;
+ case Type::I64: Write("WASM_RT_I64"); break;
+ case Type::F32: Write("WASM_RT_F32"); break;
+ case Type::F64: Write("WASM_RT_F64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(SignedType type) {
+ switch (type.type) {
+ case Type::I32: Write("s32"); break;
+ case Type::I64: Write("s64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ResultType& rt) {
+ if (!rt.types.empty()) {
+ Write(rt.types[0]);
+ } else {
+ Write("void");
+ }
+}
+
+void CWriter::Write(const Const& const_) {
+ switch (const_.type) {
+ case Type::I32:
+ Writef("%u" "u", static_cast<int32_t>(const_.u32));
+ break;
+
+ case Type::I64:
+ Writef("%" PRIu64 "ull", static_cast<int64_t>(const_.u64));
+ break;
+
+ case Type::F32: {
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((const_.f32_bits & 0x7f800000u) == 0x7f800000u) {
+ const char* sign = (const_.f32_bits & 0x80000000) ? "-" : "";
+ uint32_t significand = const_.f32_bits & 0x7fffffu;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */",
+ const_.f32_bits, sign, significand);
+ }
+ } else if (const_.f32_bits == 0x80000000) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.f");
+ } else {
+ Writef("%.9g", Bitcast<float>(const_.f32_bits));
+ }
+ break;
+ }
+
+ case Type::F64:
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((const_.f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
+ const char* sign = (const_.f64_bits & 0x8000000000000000ull) ? "-" : "";
+ uint64_t significand = const_.f64_bits & 0xfffffffffffffull;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f64_reinterpret_i64(0x%016" PRIx64 ") /* %snan:0x%013" PRIx64
+ " */",
+ const_.f64_bits, sign, significand);
+ }
+ } else if (const_.f64_bits == 0x8000000000000000ull) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.0");
+ } else {
+ Writef("%.17g", Bitcast<double>(const_.f64_bits));
+ }
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::WriteInitExpr(const ExprList& expr_list) {
+ if (expr_list.empty())
+ return;
+
+ assert(expr_list.size() == 1);
+ const Expr* expr = &expr_list.front();
+ switch (expr_list.front().type()) {
+ case ExprType::Const:
+ Write(cast<ConstExpr>(expr)->const_);
+ break;
+
+ case ExprType::GetGlobal:
+ Write(GlobalVar(cast<GetGlobalExpr>(expr)->var));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::InitGlobalSymbols() {
+ for (const char* symbol : s_global_symbols)
+ global_syms_.insert(symbol);
+}
+
+std::string CWriter::GenerateHeaderGuard() const {
+ std::string result;
+ for (char c : header_name_) {
+ if (isalnum(c) || c == '_') {
+ result += toupper(c);
+ } else {
+ result += '_';
+ }
+ }
+ result += "_GENERATED_";
+ return result;
+}
+
+void CWriter::WriteSourceTop() {
+ Write(s_source_includes);
+ Write(Newline(), "#include \"", header_name_, "\"", Newline());
+ Write(s_source_declarations);
+}
+
+void CWriter::WriteFuncTypes() {
+ Write(Newline());
+ Writef("static u32 func_types[%" PRIzd "];", module_->func_types.size());
+ Write(Newline(), Newline());
+ Write("static void init_func_types(void) {", Newline());
+ Index func_type_index = 0;
+ for (FuncType* func_type : module_->func_types) {
+ 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)));
+
+ for (Index i = 0; i < num_results; ++i)
+ Write(", ", TypeEnum(func_type->GetResultType(i)));
+
+ Write(");", Newline());
+ ++func_type_index;
+ }
+ Write("}", Newline());
+}
+
+void CWriter::WriteImports() {
+ if (module_->imports.empty())
+ return;
+
+ Write(Newline());
+
+ // TODO(binji): Write imports ordered by type.
+ for (const Import* import : module_->imports) {
+ Write("/* import: '", import->module_name, "' '", import->field_name,
+ "' */", Newline());
+ Write("extern ");
+ switch (import->kind()) {
+ case ExternalKind::Func: {
+ const Func& func = cast<FuncImport>(import)->func;
+ WriteFuncDeclaration(
+ func.decl,
+ DefineImportName(
+ func.name, import->module_name,
+ MangleFuncName(import->field_name, func.decl.sig.param_types,
+ func.decl.sig.result_types)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Global: {
+ const Global& global = cast<GlobalImport>(import)->global;
+ WriteGlobal(global,
+ DefineImportName(
+ global.name, import->module_name,
+ MangleGlobalName(import->field_name, global.type)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ const Memory& memory = cast<MemoryImport>(import)->memory;
+ WriteMemory(DefineImportName(memory.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ case ExternalKind::Table: {
+ const Table& table = cast<TableImport>(import)->table;
+ WriteTable(DefineImportName(table.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ Write(Newline());
+ }
+}
+
+void CWriter::WriteFuncDeclarations() {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import) {
+ Write("static ");
+ WriteFuncDeclaration(func->decl, DefineGlobalScopeName(func->name));
+ Write(";", Newline());
+ }
+ ++func_index;
+ }
+}
+
+void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(");
+ if (decl.GetNumParams() == 0) {
+ Write("void");
+ } else {
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ if (i != 0)
+ Write(", ");
+ Write(decl.GetParamType(i));
+ }
+ }
+ Write(")");
+}
+
+void CWriter::WriteGlobals() {
+ Index global_index = 0;
+ if (module_->globals.size() != module_->num_global_imports) {
+ Write(Newline());
+
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ Write("static ");
+ WriteGlobal(*global, DefineGlobalScopeName(global->name));
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ }
+
+ Write(Newline(), "static void init_globals(void) ", OpenBrace());
+ global_index = 0;
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ assert(!global->init_expr.empty());
+ Write(GlobalName(global->name), " = ");
+ WriteInitExpr(global->init_expr);
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteGlobal(const Global& global, const std::string& name) {
+ Write(global.type, " ", name);
+}
+
+void CWriter::WriteMemories() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ Write(Newline());
+
+ assert(module_->memories.size() <= 1);
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ Write("static ");
+ WriteMemory(DefineGlobalScopeName(memory->name));
+ Write(Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemory(const std::string& name) {
+ Write("wasm_rt_memory_t ", name, ";");
+}
+
+void CWriter::WriteTables() {
+ if (module_->tables.size() == module_->num_table_imports)
+ return;
+
+ Write(Newline());
+
+ assert(module_->tables.size() <= 1);
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ Write("static ");
+ WriteTable(DefineGlobalScopeName(table->name));
+ Write(Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTable(const std::string& name) {
+ Write("wasm_rt_table_t ", name, ";");
+}
+
+void CWriter::WriteDataInitializers() {
+ const Memory* memory = nullptr;
+ Index data_segment_index = 0;
+
+ if (!module_->memories.empty()) {
+ if (module_->data_segments.empty()) {
+ Write(Newline());
+ } else {
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write(Newline(), "static const u8 data_segment_data_",
+ data_segment_index, "[] = ", OpenBrace());
+ size_t i = 0;
+ for (uint8_t x : data_segment->data) {
+ Writef("0x%02x, ", x);
+ if ((++i % 12) == 0)
+ Write(Newline());
+ }
+ if (i > 0)
+ Write(Newline());
+ Write(CloseBrace(), ";", Newline());
+ ++data_segment_index;
+ }
+ }
+
+ memory = module_->memories[0];
+ }
+
+ Write(Newline(), "static void init_memory(void) ", OpenBrace());
+ if (memory && module_->num_memory_imports == 0) {
+ uint32_t max =
+ memory->page_limits.has_max ? memory->page_limits.max : 65536;
+ Write("wasm_rt_allocate_memory(", ExternalPtr(memory->name), ", ",
+ memory->page_limits.initial, ", ", max, ");", Newline());
+ }
+ data_segment_index = 0;
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write("memcpy(&(", ExternalRef(memory->name), ".data[");
+ WriteInitExpr(data_segment->offset);
+ Write("]), data_segment_data_", data_segment_index, ", ",
+ data_segment->data.size(), ");", Newline());
+ ++data_segment_index;
+ }
+
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteElemInitializers() {
+ const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
+
+ Write(Newline(), "static void init_table(void) ", OpenBrace());
+ Write("uint32_t offset;", Newline());
+ if (table && module_->num_table_imports == 0) {
+ uint32_t max =
+ table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX;
+ Write("wasm_rt_allocate_table(", ExternalPtr(table->name), ", ",
+ table->elem_limits.initial, ", ", max, ");", Newline());
+ }
+ Index elem_segment_index = 0;
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ Write("offset = ");
+ WriteInitExpr(elem_segment->offset);
+ Write(";", Newline());
+
+ size_t i = 0;
+ for (const Var& var : elem_segment->vars) {
+ const Func* func = module_->GetFunc(var);
+ Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var);
+
+ Write(ExternalRef(table->name), ".data[offset + ", i,
+ "] = (wasm_rt_elem_t){func_types[", func_type_index,
+ "], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), "};", Newline());
+ ++i;
+ }
+ ++elem_segment_index;
+ }
+
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteInitExports() {
+ Write(Newline(), "static void init_exports(void) ", OpenBrace());
+ WriteExports(WriteExportsKind::Initializers);
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteExports(WriteExportsKind kind) {
+ if (module_->exports.empty())
+ return;
+
+ if (kind != WriteExportsKind::Initializers) {
+ Write(Newline());
+ }
+
+ for (const Export* export_ : module_->exports) {
+ Write("/* export: '", export_->name, "' */", Newline());
+ if (kind == WriteExportsKind::Declarations) {
+ Write("extern ");
+ }
+
+ std::string mangled_name;
+ std::string internal_name;
+
+ switch (export_->kind) {
+ case ExternalKind::Func: {
+ const Func* func = module_->GetFunc(export_->var);
+ mangled_name =
+ ExportName(MangleFuncName(export_->name, func->decl.sig.param_types,
+ func->decl.sig.result_types));
+ internal_name = func->name;
+ if (kind != WriteExportsKind::Initializers) {
+ WriteFuncDeclaration(func->decl, Deref(mangled_name));
+ Write(";");
+ }
+ break;
+ }
+
+ case ExternalKind::Global: {
+ const Global* global = module_->GetGlobal(export_->var);
+ mangled_name =
+ ExportName(MangleGlobalName(export_->name, global->type));
+ internal_name = global->name;
+ if (kind != WriteExportsKind::Initializers) {
+ WriteGlobal(*global, Deref(mangled_name));
+ Write(";");
+ }
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ const Memory* memory = module_->GetMemory(export_->var);
+ mangled_name = ExportName(MangleName(export_->name));
+ internal_name = memory->name;
+ if (kind != WriteExportsKind::Initializers) {
+ WriteMemory(Deref(mangled_name));
+ }
+ break;
+ }
+
+ case ExternalKind::Table: {
+ const Table* table = module_->GetTable(export_->var);
+ mangled_name = ExportName(MangleName(export_->name));
+ internal_name = table->name;
+ if (kind != WriteExportsKind::Initializers) {
+ WriteTable(Deref(mangled_name));
+ }
+ break;
+ }
+
+ default: WABT_UNREACHABLE;
+ }
+
+ if (kind == WriteExportsKind::Initializers) {
+ Write(mangled_name, " = ", ExternalPtr(internal_name), ";");
+ }
+
+ Write(Newline());
+ }
+}
+
+void CWriter::WriteInit() {
+ Write(Newline(), "void WASM_RT_ADD_PREFIX(init)(void) ", OpenBrace());
+ Write("init_func_types();", Newline());
+ Write("init_globals();", Newline());
+ Write("init_memory();", Newline());
+ Write("init_table();", Newline());
+ Write("init_exports();", Newline());
+ for (Var* var : module_->starts) {
+ Write(ExternalRef(module_->GetFunc(*var)->name), "();", Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteFuncs() {
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import)
+ Write(Newline(), *func, Newline());
+ ++func_index;
+ }
+}
+
+void CWriter::Write(const Func& func) {
+ func_ = &func;
+ // Copy symbols from global symbol table so we don't shadow them.
+ local_syms_ = global_syms_;
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+
+ Write("static ", ResultType(func.decl.sig.result_types), " ",
+ GlobalName(func.name), "(");
+ WriteParams();
+ WriteLocals();
+ Write("FUNC_PROLOGUE;", Newline());
+
+ stream_ = &func_stream_;
+ stream_->ClearOffset();
+
+ std::string label = DefineLocalScopeName(kImplicitFuncLabel);
+ ResetTypeStack(0);
+ std::string empty; // Must not be temporary, since address is taken by Label.
+ PushLabel(LabelType::Func, empty, func.decl.sig.result_types);
+ Write(func.exprs, LabelDecl(label));
+ PopLabel();
+ ResetTypeStack(0);
+ PushTypes(func.decl.sig.result_types);
+ Write("FUNC_EPILOGUE;", Newline());
+
+ if (!func.decl.sig.result_types.empty()) {
+ // Return the top of the stack implicitly.
+ Write("return ", StackVar(0), ";", Newline());
+ }
+
+ stream_ = c_stream_;
+
+ WriteStackVarDeclarations();
+
+ std::unique_ptr<OutputBuffer> buf = func_stream_.ReleaseOutputBuffer();
+ stream_->WriteData(buf->data.data(), buf->data.size());
+
+ Write(CloseBrace());
+
+ func_stream_.Clear();
+ func_ = nullptr;
+}
+
+void CWriter::WriteParams() {
+ if (func_->decl.sig.param_types.empty()) {
+ Write("void");
+ } else {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func_->decl.sig.param_types,
+ func_->param_bindings, &index_to_name);
+ Indent(4);
+ for (Index i = 0; i < func_->GetNumParams(); ++i) {
+ if (i != 0) {
+ Write(", ");
+ if ((i % 8) == 0)
+ Write(Newline());
+ }
+ Write(func_->GetParamType(i), " ",
+ DefineLocalScopeName(index_to_name[i]));
+ }
+ Dedent(4);
+ }
+ Write(") ", OpenBrace());
+}
+
+void CWriter::WriteLocals() {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func_->local_types, func_->local_bindings,
+ &index_to_name);
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ Index local_index = 0;
+ size_t count = 0;
+ for (Type local_type : func_->local_types) {
+ if (local_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(DefineLocalScopeName(index_to_name[local_index]), " = 0");
+ ++count;
+ }
+ ++local_index;
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::WriteStackVarDeclarations() {
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ size_t count = 0;
+ for (const auto& pair: stack_var_sym_map_) {
+ Type stp_type = pair.first.second;
+ const std::string& name = pair.second;
+
+ if (stp_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(name);
+ ++count;
+ }
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::Write(const ExprList& exprs) {
+ for (const Expr& expr: exprs) {
+ switch (expr.type()) {
+ case ExprType::Binary:
+ Write(*cast<BinaryExpr>(&expr));
+ break;
+
+ case ExprType::Block: {
+ const Block& block = cast<BlockExpr>(&expr)->block;
+ std::string label = DefineLocalScopeName(block.label);
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Block, block.label, block.sig);
+ Write(block.exprs, LabelDecl(label));
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.sig);
+ break;
+ }
+
+ case ExprType::Br:
+ Write(GotoLabel(cast<BrExpr>(&expr)->var), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::BrIf:
+ Write("if (", StackVar(0), ") {");
+ DropTypes(1);
+ Write(GotoLabel(cast<BrIfExpr>(&expr)->var), "}", Newline());
+ break;
+
+ case ExprType::BrTable: {
+ const auto* bt_expr = cast<BrTableExpr>(&expr);
+ Write("switch (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ Index i = 0;
+ for (const Var& var : bt_expr->targets)
+ Write("case ", i++, ": ", GotoLabel(var), Newline());
+ Write("default: ");
+ Write(GotoLabel(bt_expr->default_target), Newline(), CloseBrace(),
+ Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+ }
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ const Func& func = *module_->GetFunc(var);
+ Index num_params = func.GetNumParams();
+ Index num_results = func.GetNumResults();
+ assert(type_stack_.size() >= num_params);
+ if (num_results > 0) {
+ assert(num_results == 1);
+ Write(StackVar(num_params - 1, func.GetResultType(0)), " = ");
+ }
+
+ Write(GlobalVar(var), "(");
+ for (Index i = 0; i < num_params; ++i) {
+ if (i != 0)
+ Write(", ");
+ Write(StackVar(num_params - i - 1));
+ }
+ Write(");", Newline());
+ DropTypes(num_params);
+ PushTypes(func.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::CallIndirect: {
+ const FuncDeclaration& decl = cast<CallIndirectExpr>(&expr)->decl;
+ Index num_params = decl.GetNumParams();
+ Index num_results = decl.GetNumResults();
+ assert(type_stack_.size() > num_params);
+ if (num_results > 0) {
+ assert(num_results == 1);
+ Write(StackVar(num_params, decl.GetResultType(0)), " = ");
+ }
+
+ assert(module_->tables.size() == 1);
+ const Table* table = module_->tables[0];
+
+ assert(decl.has_func_type);
+ Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+
+ Write("CALL_INDIRECT(", ExternalRef(table->name), ", ");
+ WriteFuncDeclaration(decl, "(*)");
+ Write(", ", func_type_index, ", ", StackVar(0));
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i));
+ }
+ Write(");", Newline());
+ DropTypes(num_params + 1);
+ PushTypes(decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Compare:
+ Write(*cast<CompareExpr>(&expr));
+ break;
+
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(&expr)->const_;
+ PushType(const_.type);
+ Write(StackVar(0), " = ", const_, ";", Newline());
+ break;
+ }
+
+ case ExprType::Convert:
+ Write(*cast<ConvertExpr>(&expr));
+ break;
+
+ case ExprType::CurrentMemory: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ PushType(Type::I32);
+ Write(StackVar(0), " = ", ExternalRef(memory->name), ".pages;",
+ Newline());
+ break;
+ }
+
+ case ExprType::Drop:
+ DropTypes(1);
+ break;
+
+ case ExprType::GetGlobal: {
+ const Var& var = cast<GetGlobalExpr>(&expr)->var;
+ PushType(module_->GetGlobal(var)->type);
+ Write(StackVar(0), " = ", GlobalVar(var), ";", Newline());
+ break;
+ }
+
+ case ExprType::GetLocal: {
+ const Var& var = cast<GetLocalExpr>(&expr)->var;
+ PushType(func_->GetLocalType(var));
+ Write(StackVar(0), " = ", var, ";", Newline());
+ break;
+ }
+
+ case ExprType::GrowMemory: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(StackVar(0), " = wasm_rt_grow_memory(", ExternalPtr(memory->name),
+ ", ", StackVar(0), ");", Newline());
+ break;
+ }
+
+ case ExprType::If: {
+ const IfExpr& if_ = *cast<IfExpr>(&expr);
+ Write("if (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ std::string label = DefineLocalScopeName(if_.true_.label);
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::If, if_.true_.label, if_.true_.sig);
+ Write(if_.true_.exprs, CloseBrace());
+ if (!if_.false_.empty()) {
+ ResetTypeStack(mark);
+ Write(" else ", OpenBrace(), if_.false_, CloseBrace());
+ }
+ ResetTypeStack(mark);
+ Write(Newline(), LabelDecl(label));
+ PopLabel();
+ PushTypes(if_.true_.sig);
+ break;
+ }
+
+ case ExprType::Load:
+ Write(*cast<LoadExpr>(&expr));
+ break;
+
+ case ExprType::Loop: {
+ const Block& block = cast<LoopExpr>(&expr)->block;
+ if (!block.exprs.empty()) {
+ Write(DefineLocalScopeName(block.label), ": ");
+ Indent();
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Loop, block.label, block.sig);
+ Write(Newline(), block.exprs);
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.sig);
+ Dedent();
+ }
+ break;
+ }
+
+ case ExprType::Nop:
+ break;
+
+ case ExprType::Return:
+ // Goto the function label instead; this way we can do shared function
+ // cleanup code in one place.
+ Write(GotoLabel(Var(label_stack_.size() - 1)), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::Select: {
+ Type type = StackType(1);
+ Write(StackVar(2), " = ", StackVar(0), " ? ", StackVar(2), " : ",
+ StackVar(1), ";", Newline());
+ DropTypes(3);
+ PushType(type);
+ break;
+ }
+
+ case ExprType::SetGlobal: {
+ const Var& var = cast<SetGlobalExpr>(&expr)->var;
+ Write(GlobalVar(var), " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::SetLocal: {
+ const Var& var = cast<SetLocalExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::Store:
+ Write(*cast<StoreExpr>(&expr));
+ break;
+
+ case ExprType::TeeLocal: {
+ const Var& var = cast<TeeLocalExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ break;
+ }
+
+ case ExprType::Unary:
+ Write(*cast<UnaryExpr>(&expr));
+ break;
+
+ case ExprType::Unreachable:
+ Write("UNREACHABLE;", Newline());
+ return;
+
+ case ExprType::AtomicWait:
+ case ExprType::AtomicWake:
+ case ExprType::AtomicLoad:
+ case ExprType::AtomicRmw:
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicStore:
+ case ExprType::Rethrow:
+ case ExprType::Throw:
+ case ExprType::TryBlock:
+ UNIMPLEMENTED("...");
+ break;
+ }
+ }
+}
+
+void CWriter::WriteSimpleUnaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", op, "(", StackVar(0), ");", Newline());
+ DropTypes(1);
+ PushType(opcode.GetResultType());
+}
+
+void CWriter::WriteInfixBinaryExpr(Opcode opcode,
+ const char* op,
+ AssignOp assign_op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type));
+ if (assign_op == AssignOp::Allowed) {
+ Write(" ", op, "= ", StackVar(0));
+ } else {
+ Write(" = ", StackVar(1), " ", op, " ", StackVar(0));
+ }
+ Write(";", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WritePrefixBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", op, "(", StackVar(1), ", ",
+ StackVar(0), ");", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WriteSignedBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Type type = opcode.GetParamType1();
+ assert(opcode.GetParamType2() == type);
+ Write(StackVar(1, result_type), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " ", op, " (", SignedType(type), ")", StackVar(0), ");",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const BinaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Add:
+ case Opcode::I64Add:
+ case Opcode::F32Add:
+ case Opcode::F64Add:
+ WriteInfixBinaryExpr(expr.opcode, "+");
+ break;
+
+ case Opcode::I32Sub:
+ case Opcode::I64Sub:
+ case Opcode::F32Sub:
+ case Opcode::F64Sub:
+ WriteInfixBinaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::I32Mul:
+ case Opcode::I64Mul:
+ case Opcode::F32Mul:
+ case Opcode::F64Mul:
+ WriteInfixBinaryExpr(expr.opcode, "*");
+ break;
+
+ case Opcode::I32DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_DIV_S");
+ break;
+
+ case Opcode::I64DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_DIV_S");
+ break;
+
+ case Opcode::I32DivU:
+ case Opcode::I64DivU:
+ WritePrefixBinaryExpr(expr.opcode, "DIV_U");
+ break;
+
+ case Opcode::F32Div:
+ case Opcode::F64Div:
+ WriteInfixBinaryExpr(expr.opcode, "/");
+ break;
+
+ case Opcode::I32RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_REM_S");
+ break;
+
+ case Opcode::I64RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_REM_S");
+ break;
+
+ case Opcode::I32RemU:
+ case Opcode::I64RemU:
+ WritePrefixBinaryExpr(expr.opcode, "REM_U");
+ break;
+
+ case Opcode::I32And:
+ case Opcode::I64And:
+ WriteInfixBinaryExpr(expr.opcode, "&");
+ break;
+
+ case Opcode::I32Or:
+ case Opcode::I64Or:
+ WriteInfixBinaryExpr(expr.opcode, "|");
+ break;
+
+ case Opcode::I32Xor:
+ case Opcode::I64Xor:
+ WriteInfixBinaryExpr(expr.opcode, "^");
+ break;
+
+ case Opcode::I32Shl:
+ case Opcode::I64Shl:
+ Write(StackVar(1), " <<= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32ShrS:
+ case Opcode::I64ShrS: {
+ Type type = expr.opcode.GetResultType();
+ Write(StackVar(1), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " >> (", StackVar(0), " & ", GetShiftMask(type), "));",
+ Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case Opcode::I32ShrU:
+ case Opcode::I64ShrU:
+ Write(StackVar(1), " >>= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTL");
+ break;
+
+ case Opcode::I64Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTL");
+ break;
+
+ case Opcode::I32Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTR");
+ break;
+
+ case Opcode::I64Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTR");
+ break;
+
+ case Opcode::F32Min:
+ case Opcode::F64Min:
+ WritePrefixBinaryExpr(expr.opcode, "FMIN");
+ break;
+
+ case Opcode::F32Max:
+ case Opcode::F64Max:
+ WritePrefixBinaryExpr(expr.opcode, "FMAX");
+ break;
+
+ case Opcode::F32Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysignf");
+ break;
+
+ case Opcode::F64Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysign");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const CompareExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eq:
+ case Opcode::I64Eq:
+ case Opcode::F32Eq:
+ case Opcode::F64Eq:
+ WriteInfixBinaryExpr(expr.opcode, "==", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32Ne:
+ case Opcode::I64Ne:
+ case Opcode::F32Ne:
+ case Opcode::F64Ne:
+ WriteInfixBinaryExpr(expr.opcode, "!=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LtS:
+ case Opcode::I64LtS:
+ WriteSignedBinaryExpr(expr.opcode, "<");
+ break;
+
+ case Opcode::I32LtU:
+ case Opcode::I64LtU:
+ case Opcode::F32Lt:
+ case Opcode::F64Lt:
+ WriteInfixBinaryExpr(expr.opcode, "<", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LeS:
+ case Opcode::I64LeS:
+ WriteSignedBinaryExpr(expr.opcode, "<=");
+ break;
+
+ case Opcode::I32LeU:
+ case Opcode::I64LeU:
+ case Opcode::F32Le:
+ case Opcode::F64Le:
+ WriteInfixBinaryExpr(expr.opcode, "<=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GtS:
+ case Opcode::I64GtS:
+ WriteSignedBinaryExpr(expr.opcode, ">");
+ break;
+
+ case Opcode::I32GtU:
+ case Opcode::I64GtU:
+ case Opcode::F32Gt:
+ case Opcode::F64Gt:
+ WriteInfixBinaryExpr(expr.opcode, ">", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GeS:
+ case Opcode::I64GeS:
+ WriteSignedBinaryExpr(expr.opcode, ">=");
+ break;
+
+ case Opcode::I32GeU:
+ case Opcode::I64GeU:
+ case Opcode::F32Ge:
+ case Opcode::F64Ge:
+ WriteInfixBinaryExpr(expr.opcode, ">=", AssignOp::Disallowed);
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ConvertExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eqz:
+ case Opcode::I64Eqz:
+ WriteSimpleUnaryExpr(expr.opcode, "!");
+ break;
+
+ case Opcode::I64ExtendSI32:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)");
+ break;
+
+ case Opcode::I64ExtendUI32:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)");
+ break;
+
+ case Opcode::I32WrapI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)");
+ break;
+
+ case Opcode::I32TruncSF32:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F32");
+ break;
+
+ case Opcode::I64TruncSF32:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F32");
+ break;
+
+ case Opcode::I32TruncSF64:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F64");
+ break;
+
+ case Opcode::I64TruncSF64:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F64");
+ break;
+
+ case Opcode::I32TruncUF32:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F32");
+ break;
+
+ case Opcode::I64TruncUF32:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F32");
+ break;
+
+ case Opcode::I32TruncUF64:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F64");
+ break;
+
+ case Opcode::I64TruncUF64:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F64");
+ break;
+
+ case Opcode::I32TruncSSatF32:
+ case Opcode::I64TruncSSatF32:
+ case Opcode::I32TruncSSatF64:
+ case Opcode::I64TruncSSatF64:
+ case Opcode::I32TruncUSatF32:
+ case Opcode::I64TruncUSatF32:
+ case Opcode::I32TruncUSatF64:
+ case Opcode::I64TruncUSatF64:
+ UNIMPLEMENTED(expr.opcode.GetName());
+ break;
+
+ case Opcode::F32ConvertSI32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s32)");
+ break;
+
+ case Opcode::F32ConvertSI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s64)");
+ break;
+
+ case Opcode::F32ConvertUI32:
+ case Opcode::F32DemoteF64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F32ConvertUI64:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_float).
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F64ConvertSI32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s32)");
+ break;
+
+ case Opcode::F64ConvertSI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s64)");
+ break;
+
+ case Opcode::F64ConvertUI32:
+ case Opcode::F64PromoteF32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F64ConvertUI64:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_double).
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F32ReinterpretI32:
+ WriteSimpleUnaryExpr(expr.opcode, "f32_reinterpret_i32");
+ break;
+
+ case Opcode::I32ReinterpretF32:
+ WriteSimpleUnaryExpr(expr.opcode, "i32_reinterpret_f32");
+ break;
+
+ case Opcode::F64ReinterpretI64:
+ WriteSimpleUnaryExpr(expr.opcode, "f64_reinterpret_i64");
+ break;
+
+ case Opcode::I64ReinterpretF64:
+ WriteSimpleUnaryExpr(expr.opcode, "i64_reinterpret_f64");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const LoadExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Load: func = "i32_load"; break;
+ case Opcode::I64Load: func = "i64_load"; break;
+ case Opcode::F32Load: func = "f32_load"; break;
+ case Opcode::F64Load: func = "f64_load"; break;
+ case Opcode::I32Load8S: func = "i32_load8_s"; break;
+ case Opcode::I64Load8S: func = "i64_load8_s"; break;
+ case Opcode::I32Load8U: func = "i32_load8_u"; break;
+ case Opcode::I64Load8U: func = "i64_load8_u"; break;
+ case Opcode::I32Load16S: func = "i32_load16_s"; break;
+ case Opcode::I64Load16S: func = "i64_load16_s"; break;
+ case Opcode::I32Load16U: func = "i32_load16_u"; break;
+ case Opcode::I64Load16U: func = "i32_load16_u"; break;
+ case Opcode::I64Load32S: func = "i64_load32_s"; break;
+ case Opcode::I64Load32U: func = "i64_load32_u"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(", ExternalPtr(memory->name),
+ ", (u64)(", StackVar(0));
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write("));", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const StoreExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Store: func = "i32_store"; break;
+ case Opcode::I64Store: func = "i64_store"; break;
+ case Opcode::F32Store: func = "f32_store"; break;
+ case Opcode::F64Store: func = "f64_store"; break;
+ case Opcode::I32Store8: func = "i32_store8"; break;
+ case Opcode::I64Store8: func = "i64_store8"; break;
+ case Opcode::I32Store16: func = "i32_store16"; break;
+ case Opcode::I64Store16: func = "i64_store16"; break;
+ case Opcode::I64Store32: func = "i64_store32"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(func, "(", ExternalPtr(memory->name), ", (u64)(", StackVar(1));
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write("), ", StackVar(0), ");", Newline());
+ DropTypes(2);
+}
+
+void CWriter::Write(const UnaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CLZ");
+ break;
+
+ case Opcode::I64Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CLZ");
+ break;
+
+ case Opcode::I32Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CTZ");
+ break;
+
+ case Opcode::I64Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CTZ");
+ break;
+
+ case Opcode::I32Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_POPCNT");
+ break;
+
+ case Opcode::I64Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_POPCNT");
+ break;
+
+ case Opcode::F32Neg:
+ case Opcode::F64Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::F32Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabsf");
+ break;
+
+ case Opcode::F64Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabs");
+ break;
+
+ case Opcode::F32Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrtf");
+ break;
+
+ case Opcode::F64Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrt");
+ break;
+
+ case Opcode::F32Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceilf");
+ break;
+
+ case Opcode::F64Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceil");
+ break;
+
+ case Opcode::F32Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floorf");
+ break;
+
+ case Opcode::F64Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floor");
+ break;
+
+ case Opcode::F32Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "truncf");
+ break;
+
+ case Opcode::F64Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "trunc");
+ break;
+
+ case Opcode::F32Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyintf");
+ break;
+
+ case Opcode::F64Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyint");
+ break;
+
+ case Opcode::I32Extend8S:
+ case Opcode::I32Extend16S:
+ case Opcode::I64Extend8S:
+ case Opcode::I64Extend16S:
+ case Opcode::I64Extend32S:
+ UNIMPLEMENTED(expr.opcode.GetName());
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::WriteCHeader() {
+ stream_ = h_stream_;
+ std::string guard = GenerateHeaderGuard();
+ Write("#ifndef ", guard, Newline());
+ Write("#define ", guard, Newline());
+ Write(s_header_top);
+ WriteImports();
+ WriteExports(WriteExportsKind::Declarations);
+ Write(s_header_bottom);
+ Write(Newline(), "#endif /* ", guard, " */", Newline());
+}
+
+void CWriter::WriteCSource() {
+ stream_ = c_stream_;
+ WriteSourceTop();
+ WriteFuncTypes();
+ WriteFuncDeclarations();
+ WriteGlobals();
+ WriteMemories();
+ WriteTables();
+ WriteFuncs();
+ WriteDataInitializers();
+ WriteElemInitializers();
+ WriteExports(WriteExportsKind::Definitions);
+ WriteInitExports();
+ WriteInit();
+}
+
+Result CWriter::WriteModule(const Module& module) {
+ WABT_USE(options_);
+ module_ = &module;
+ InitGlobalSymbols();
+ WriteCHeader();
+ WriteCSource();
+ return result_;
+}
+
+
+} // end anonymous namespace
+
+Result WriteC(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const Module* module,
+ const WriteCOptions* options) {
+ CWriter c_writer(c_stream, h_stream, header_name, options);
+ return c_writer.WriteModule(*module);
+}
+
+} // namespace wabt
diff --git a/src/c-writer.h b/src/c-writer.h
new file mode 100644
index 00000000..3b4278aa
--- /dev/null
+++ b/src/c-writer.h
@@ -0,0 +1,37 @@
+/*
+ * 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_C_WRITER_H_
+#define WABT_C_WRITER_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+struct Module;
+class Stream;
+
+struct WriteCOptions {};
+
+Result WriteC(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const Module*,
+ const WriteCOptions*);
+
+} // namespace wabt
+
+#endif /* WABT_C_WRITER_H_ */
diff --git a/src/ir.cc b/src/ir.cc
index 8482f281..ad9ae4bd 100644
--- a/src/ir.cc
+++ b/src/ir.cc
@@ -134,6 +134,21 @@ bool Module::IsImport(ExternalKind kind, const Var& var) const {
}
}
+Type Func::GetLocalType(Index index) const {
+ Index num_params = decl.GetNumParams();
+ if (index < num_params) {
+ return GetParamType(index);
+ } else {
+ index -= num_params;
+ assert(index < local_types.size());
+ return local_types[index];
+ }
+}
+
+Type Func::GetLocalType(const Var& var) const {
+ return GetLocalType(GetLocalIndex(var));
+}
+
Index Func::GetLocalIndex(const Var& var) const {
if (var.is_index()) {
return var.index();
@@ -177,6 +192,10 @@ Global* Module::GetGlobal(const Var& var) {
return globals[index];
}
+const Table* Module::GetTable(const Var& var) const {
+ return const_cast<Module*>(this)->GetTable(var);
+}
+
Table* Module::GetTable(const Var& var) {
Index index = table_bindings.FindIndex(var);
if (index >= tables.size()) {
@@ -185,6 +204,10 @@ Table* Module::GetTable(const Var& var) {
return tables[index];
}
+const Memory* Module::GetMemory(const Var& var) const {
+ return const_cast<Module*>(this)->GetMemory(var);
+}
+
Memory* Module::GetMemory(const Var& var) {
Index index = memory_bindings.FindIndex(var);
if (index >= memories.size()) {
diff --git a/src/ir.h b/src/ir.h
index 0db30a6c..4e6284a2 100644
--- a/src/ir.h
+++ b/src/ir.h
@@ -393,6 +393,8 @@ struct Func {
Type GetParamType(Index index) const { return decl.GetParamType(index); }
Type GetResultType(Index index) const { return decl.GetResultType(index); }
+ Type GetLocalType(Index index) const;
+ Type GetLocalType(const Var& var) const;
Index GetNumParams() const { return decl.GetNumParams(); }
Index GetNumLocals() const { return local_types.size(); }
Index GetNumParamsAndLocals() const {
@@ -674,8 +676,10 @@ struct Module {
const Func* GetFunc(const Var&) const;
Func* GetFunc(const Var&);
Index GetTableIndex(const Var&) const;
+ const Table* GetTable(const Var&) const;
Table* GetTable(const Var&);
Index GetMemoryIndex(const Var&) const;
+ const Memory* GetMemory(const Var&) const;
Memory* GetMemory(const Var&);
Index GetGlobalIndex(const Var&) const;
const Global* GetGlobal(const Var&) const;
diff --git a/src/stream.cc b/src/stream.cc
index bb9b7a3b..a127e706 100644
--- a/src/stream.cc
+++ b/src/stream.cc
@@ -154,6 +154,13 @@ std::unique_ptr<OutputBuffer> MemoryStream::ReleaseOutputBuffer() {
return std::move(buf_);
}
+void MemoryStream::Clear() {
+ if (buf_)
+ buf_->clear();
+ else
+ buf_.reset(new OutputBuffer());
+}
+
Result MemoryStream::WriteDataImpl(size_t dst_offset,
const void* src,
size_t size) {
diff --git a/src/stream.h b/src/stream.h
index 75a082ea..8a0f1c80 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -52,6 +52,7 @@ class Stream {
bool has_log_stream() const { return log_stream_ != nullptr; }
+ void ClearOffset() { offset_ = 0; }
void AddOffset(ssize_t delta);
void WriteData(const void* src,
@@ -147,6 +148,7 @@ class Stream {
struct OutputBuffer {
Result WriteToFile(string_view filename) const;
+ void clear() { data.clear(); }
size_t size() const { return data.size(); }
std::vector<uint8_t> data;
@@ -162,6 +164,8 @@ class MemoryStream : public Stream {
OutputBuffer& output_buffer() { return *buf_; }
std::unique_ptr<OutputBuffer> ReleaseOutputBuffer();
+ void Clear();
+
Result WriteToFile(string_view filename) {
return buf_->WriteToFile(filename);
}
diff --git a/src/tools/wasm2c.cc b/src/tools/wasm2c.cc
new file mode 100644
index 00000000..15981c93
--- /dev/null
+++ b/src/tools/wasm2c.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-handler.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+
+#include "src/c-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static Features s_features;
+static WriteCOptions s_write_c_options;
+static bool s_read_debug_names = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and convert it to
+ a C source file and header.
+
+examples:
+ # parse binary file test.wasm and write test.c and test.h
+ $ wasm2c test.wasm -o test.c
+
+ # parse test.wasm, write test.c and test.h, but ignore the debug names, if any
+ $ wasm2c test.wasm --no-debug-names -o test.c
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2c", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStdout();
+ });
+ parser.AddHelpOption();
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated C source file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+}
+
+// TODO(binji): copied from binary-writer-spec.cc, probably should share.
+static string_view strip_extension(string_view s) {
+ string_view ext = s.substr(s.find_last_of('.'));
+ string_view result = s;
+
+ if (ext == ".c")
+ result.remove_suffix(ext.length());
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ ErrorHandlerFile error_handler(Location::Type::Binary);
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ &options, &error_handler, &module);
+ if (Succeeded(result)) {
+ if (Succeeded(result)) {
+ ValidateOptions options(s_features);
+ WastLexer* lexer = nullptr;
+ result = ValidateModule(lexer, &module, &error_handler, &options);
+ result |= GenerateNames(&module);
+ }
+
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+
+ if (Succeeded(result)) {
+ if (!s_outfile.empty()) {
+ std::string header_name =
+ strip_extension(s_outfile).to_string() + ".h";
+ FileStream c_stream(s_outfile.c_str());
+ FileStream h_stream(header_name);
+ result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module,
+ &s_write_c_options);
+ } else {
+ FileStream stream(stdout);
+ result =
+ WriteC(&stream, &stream, "wasm.h", &module, &s_write_c_options);
+ }
+ }
+ }
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
+
diff --git a/test/README.md b/test/README.md
index 0e6a3d08..f88fc26a 100644
--- a/test/README.md
+++ b/test/README.md
@@ -136,9 +136,13 @@ The currently supported list of tools (see
interpreter
- `run-opcodecnt`: parse a wasm text file, convert it to binary, then display
opcode usage counts.
-- `run-gen-spec-js`:parse wasm spec test text file, convert it to a JSON file
+- `run-gen-spec-js`: parse wasm spec test text file, convert it to a JSON file
and a collection of `.wasm` and `.wast` files, then take all of these files
and generate a JavaScript file that will execute the same tests.
+- `run-spec-wasm2c`: similar to `run-gen-spec-js`, but the output instead will
+ be C source files, that are then compiled with the default C compiler (`cc`).
+ Finally, the native executable is run.
+
## Test subdirectories
diff --git a/test/find_exe.py b/test/find_exe.py
index 5bc919a6..8bc19d41 100644
--- a/test/find_exe.py
+++ b/test/find_exe.py
@@ -26,7 +26,7 @@ REPO_ROOT_DIR = os.path.dirname(SCRIPT_DIR)
EXECUTABLES = [
'wat2wasm', 'wast2json', 'wasm2wat', 'wasm-objdump', 'wasm-interp',
'wasm-opcodecnt', 'wat-desugar', 'wasm-link', 'spectest-interp',
- 'wasm-validate',
+ 'wasm-validate', 'wasm2c',
]
@@ -103,3 +103,7 @@ def GetWatDesugarExecutable(override=None):
def GetWasmValidateExecutable(override=None):
return FindExecutable('wasm-validate', override)
+
+
+def GetWasm2CExecutable(override=None):
+ return FindExecutable('wasm2c', override)
diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py
new file mode 100755
index 00000000..0a2d3e07
--- /dev/null
+++ b/test/run-spec-wasm2c.py
@@ -0,0 +1,398 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+from __future__ import print_function
+import argparse
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+import json
+import os
+import re
+import struct
+import subprocess
+import sys
+
+import find_exe
+import utils
+from utils import Error
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def ReinterpretF32(f32_bits):
+ return struct.unpack('<f', struct.pack('<I', f32_bits))[0]
+
+def F32ToC(f32_bits):
+ F32_SIGN_BIT = 0x80000000
+ F32_INF = 0x7f800000
+ F32_SIG_MASK = 0x7fffff
+
+ if (f32_bits & F32_INF) == F32_INF:
+ sign = '-' if (f32_bits & F32_SIGN_BIT) == F32_SIGN_BIT else ''
+ # NaN or infinity
+ if f32_bits & F32_SIG_MASK:
+ # NaN
+ return '%smake_nan_f32(0x%06x)' % (sign, f32_bits & F32_SIG_MASK)
+ else:
+ return '%sINFINITY' % sign
+ elif f32_bits == F32_SIGN_BIT:
+ return '-0.f'
+ else:
+ s = '%.9g' % ReinterpretF32(f32_bits)
+ if '.' not in s:
+ s += '.'
+ return s + 'f'
+
+
+def ReinterpretF64(f64_bits):
+ return struct.unpack('<d', struct.pack('<Q', f64_bits))[0]
+
+def F64ToC(f64_bits):
+ F64_SIGN_BIT = 0x8000000000000000
+ F64_INF = 0x7ff0000000000000
+ F64_SIG_MASK = 0xfffffffffffff
+
+ if (f64_bits & F64_INF) == F64_INF:
+ sign = '-' if (f64_bits & F64_SIGN_BIT) == F64_SIGN_BIT else ''
+ # NaN or infinity
+ if f64_bits & F64_SIG_MASK:
+ # NaN
+ return '%smake_nan_f64(0x%06x)' % (sign, f64_bits & F64_SIG_MASK)
+ else:
+ return '%sINFINITY' % sign
+ elif f64_bits == F64_SIGN_BIT:
+ return '-0.0'
+ else:
+ return '%.17g' % ReinterpretF64(f64_bits)
+
+
+def MangleType(t):
+ return {'i32': 'i', 'i64': 'j', 'f32': 'f', 'f64': 'd'}[t]
+
+
+def MangleTypes(types):
+ if not types:
+ return 'v'
+ return ''.join(MangleType(t) for t in types)
+
+
+def MangleName(s):
+ result = 'Z_'
+ for c in s:
+ # NOTE(binji): Z is not allowed.
+ if c in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789':
+ result += c
+ else:
+ result += 'Z%02X' % ord(c)
+ return result
+
+
+class CWriter(object):
+
+ def __init__(self, spec_json, prefix, out_file, out_dir):
+ self.source_filename = os.path.basename(spec_json['source_filename'])
+ self.commands = spec_json['commands']
+ self.out_file = out_file
+ self.out_dir = out_dir
+ self.prefix = prefix
+ self.module_idx = 0
+ self.module_name_to_idx = {}
+ self.module_prefix_map = {}
+
+ def Write(self):
+ self._MaybeWriteDummyModule()
+ self._CacheModulePrefixes()
+ self._WriteIncludes()
+ self.out_file.write(self.prefix)
+ self.out_file.write("\nvoid run_spec_tests(void) {\n\n")
+ for command in self.commands:
+ self._WriteCommand(command)
+ self.out_file.write("\n}\n")
+
+ def GetModuleFilenames(self):
+ return [c['filename'] for c in self.commands if c['type'] == 'module']
+
+ def GetModulePrefix(self, idx_or_name=None):
+ if idx_or_name is not None:
+ return self.module_prefix_map[idx_or_name]
+ return self.module_prefix_map[self.module_idx - 1]
+
+ def _CacheModulePrefixes(self):
+ idx = 0
+ for command in self.commands:
+ if command['type'] == 'module':
+ name = os.path.basename(command['filename'])
+ name = os.path.splitext(name)[0]
+ name = re.sub(r'[^a-zA-Z0-9_]', '_', name)
+ name = MangleName(name)
+
+ self.module_prefix_map[idx] = name
+
+ if 'name' in command:
+ self.module_name_to_idx[command['name']] = idx
+ self.module_prefix_map[command['name']] = name
+
+ idx += 1
+ elif command['type'] == 'register':
+ name = MangleName(command['as'])
+ if 'name' in command:
+ self.module_prefix_map[command['name']] = name
+ name_idx = self.module_name_to_idx[command['name']]
+ else:
+ name_idx = idx - 1
+
+ self.module_prefix_map[name_idx] = name
+
+ def _MaybeWriteDummyModule(self):
+ if len(self.GetModuleFilenames()) == 0:
+ # This test doesn't have any valid modules, so just use a dummy instead.
+ filename = utils.ChangeExt(self.source_filename, '-dummy.wasm')
+ with open(os.path.join(self.out_dir, filename), 'wb') as wasm_file:
+ wasm_file.write(b'\x00\x61\x73\x6d\x01\x00\x00\x00')
+
+ dummy_command = {'type': 'module', 'line': 0, 'filename': filename}
+ self.commands.insert(0, dummy_command)
+
+ def _WriteFileAndLine(self, command):
+ self.out_file.write('// %s:%d\n' % (self.source_filename, command['line']))
+
+ def _WriteIncludes(self):
+ idx = 0
+ for filename in self.GetModuleFilenames():
+ header = os.path.splitext(filename)[0] + '.h'
+ self.out_file.write(
+ '#define WASM_RT_MODULE_PREFIX %s\n' % self.GetModulePrefix(idx))
+ self.out_file.write("#include \"%s\"\n" % header)
+ self.out_file.write('#undef WASM_RT_MODULE_PREFIX\n\n')
+ idx += 1
+
+ def _WriteCommand(self, command):
+ command_funcs = {
+ 'module': self._WriteModuleCommand,
+ 'action': self._WriteActionCommand,
+ 'assert_return': self._WriteAssertReturnCommand,
+ 'assert_return_canonical_nan': self._WriteAssertReturnNanCommand,
+ 'assert_return_arithmetic_nan': self._WriteAssertReturnNanCommand,
+ 'assert_trap': self._WriteAssertActionCommand,
+ 'assert_exhaustion': self._WriteAssertActionCommand,
+ }
+
+ func = command_funcs.get(command['type'])
+ if func is not None:
+ self._WriteFileAndLine(command)
+ func(command)
+ self.out_file.write('\n')
+
+ def _WriteModuleCommand(self, command):
+ self.module_idx += 1
+ self.out_file.write('%sinit();\n' % self.GetModulePrefix())
+
+ def _WriteActionCommand(self, command):
+ self.out_file.write('%s;\n' % self._Action(command))
+
+ def _WriteAssertReturnCommand(self, command):
+ expected = command['expected']
+ if len(expected) == 1:
+ assert_map = {
+ 'i32': 'ASSERT_RETURN_I32',
+ 'f32': 'ASSERT_RETURN_F32',
+ 'i64': 'ASSERT_RETURN_I64',
+ 'f64': 'ASSERT_RETURN_F64',
+ }
+
+ type_ = expected[0]['type']
+ assert_macro = assert_map[type_]
+ self.out_file.write('%s(%s, %s);\n' %
+ (assert_macro,
+ self._Action(command),
+ self._ConstantList(expected)))
+ elif len(expected) == 0:
+ self._WriteAssertActionCommand(command)
+ else:
+ raise Error('Unexpected result with multiple values: %s' % expected)
+
+ def _WriteAssertReturnNanCommand(self, command):
+ assert_map = {
+ ('assert_return_canonical_nan', 'f32'): 'ASSERT_RETURN_CANONICAL_NAN_F32',
+ ('assert_return_canonical_nan', 'f64'): 'ASSERT_RETURN_CANONICAL_NAN_F64',
+ ('assert_return_arithmetic_nan', 'f32'): 'ASSERT_RETURN_ARITHMETIC_NAN_F32',
+ ('assert_return_arithmetic_nan', 'f64'): 'ASSERT_RETURN_ARITHMETIC_NAN_F64',
+ }
+
+ expected = command['expected']
+ type_ = expected[0]['type']
+ assert_macro = assert_map[(command['type'], type_)]
+
+ self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command)))
+
+ def _WriteAssertActionCommand(self, command):
+ assert_map = {
+ 'assert_exhaustion': 'ASSERT_EXHAUSTION',
+ 'assert_return': 'ASSERT_RETURN',
+ 'assert_trap': 'ASSERT_TRAP',
+ }
+
+ assert_macro = assert_map[command['type']]
+ self.out_file.write('%s(%s);\n' % (assert_macro, self._Action(command)))
+
+ def _Constant(self, const):
+ type_ = const['type']
+ value = int(const['value'])
+ if type_ == 'i32':
+ return '%su' % value
+ elif type_ == 'i64':
+ return '%sull' % value
+ elif type_ == 'f32':
+ return F32ToC(value)
+ elif type_ == 'f64':
+ return F64ToC(value)
+ else:
+ assert False
+
+ def _ConstantList(self, consts):
+ return ', '.join(self._Constant(const) for const in consts)
+
+ def _ActionSig(self, action, expected):
+ type_ = action['type']
+ result_types = [result['type'] for result in expected]
+ arg_types = [arg['type'] for arg in action.get('args', [])]
+ if type_ == 'invoke':
+ return MangleTypes(result_types) + MangleTypes(arg_types)
+ elif type_ == 'get':
+ return MangleType(result_types[0])
+ else:
+ raise Error('Unexpected action type: %s' % type_)
+
+ def _Action(self, command):
+ action = command['action']
+ expected = command['expected']
+ type_ = action['type']
+ mangled_module_name = self.GetModulePrefix(action.get('module'))
+ field = (mangled_module_name + MangleName(action['field']) +
+ MangleName(self._ActionSig(action, expected)))
+ if type_ == 'invoke':
+ return '%s(%s)' % (field, self._ConstantList(action.get('args', [])))
+ elif type_ == 'get':
+ return '*%s' % field
+ else:
+ raise Error('Unexpected action type: %s' % type_)
+
+
+def main(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-o', '--out-dir', metavar='PATH',
+ help='output directory for files.')
+ parser.add_argument('-P', '--prefix', metavar='PATH', help='prefix file.',
+ default=os.path.join(SCRIPT_DIR, 'spec-wasm2c-prefix.c'))
+ parser.add_argument('--bindir', metavar='PATH',
+ default=find_exe.GetDefaultPath(),
+ help='directory to search for all executables.')
+ parser.add_argument('--cc', metavar='PATH',
+ help='the path to the C compiler', default='cc')
+ parser.add_argument('--cflags', metavar='FLAGS',
+ help='additional flags for C compiler.',
+ action='append', default=[])
+ parser.add_argument('--compile', help='compile the C code (default)',
+ dest='compile', action='store_true')
+ parser.add_argument('--no-compile', help='don\'t compile the C code',
+ dest='compile', action='store_false')
+ parser.set_defaults(compile=True)
+ parser.add_argument('--no-run', help='don\'t run the compiled executable',
+ dest='run', action='store_false')
+ parser.add_argument('-v', '--verbose', help='print more diagnotic messages.',
+ action='store_true')
+ parser.add_argument('--no-error-cmdline',
+ help='don\'t display the subprocess\'s commandline when'
+ + ' an error occurs', dest='error_cmdline',
+ action='store_false')
+ parser.add_argument('-p', '--print-cmd',
+ help='print the commands that are run.',
+ action='store_true')
+ parser.add_argument('file', help='wast file.')
+ options = parser.parse_args(args)
+
+ with utils.TempDirectory(options.out_dir, 'run-spec-wasm2c-') as out_dir:
+ # Parse JSON file and generate main .c file with calls to test functions.
+ wast2json = utils.Executable(
+ find_exe.GetWast2JsonExecutable(options.bindir),
+ error_cmdline=options.error_cmdline)
+ wast2json.AppendOptionalArgs({'-v': options.verbose})
+
+ json_file_path = utils.ChangeDir(
+ utils.ChangeExt(options.file, '.json'), out_dir)
+ wast2json.RunWithArgs(options.file, '-o', json_file_path)
+
+ wasm2c = utils.Executable(
+ find_exe.GetWasm2CExecutable(options.bindir),
+ error_cmdline=options.error_cmdline)
+
+ cc = utils.Executable(options.cc, *options.cflags)
+
+ with open(json_file_path) as json_file:
+ spec_json = json.load(json_file)
+
+ prefix = ''
+ if options.prefix:
+ with open(options.prefix) as prefix_file:
+ prefix = prefix_file.read() + '\n'
+
+ output = StringIO()
+ cwriter = CWriter(spec_json, prefix, output, out_dir)
+ cwriter.Write()
+
+ main_filename = utils.ChangeExt(json_file_path, '-main.c')
+ with open(main_filename, 'w') as out_main_file:
+ out_main_file.write(output.getvalue())
+
+ o_filenames = []
+
+ for i, wasm_filename in enumerate(cwriter.GetModuleFilenames()):
+ c_filename = utils.ChangeExt(wasm_filename, '.c')
+ wasm2c.RunWithArgs(wasm_filename, '-o', c_filename, cwd=out_dir)
+ if options.compile:
+ o_filename = utils.ChangeExt(wasm_filename, '.o')
+ o_filenames.append(o_filename)
+ cc.RunWithArgs(
+ '-c', '-o', o_filename,
+ '-DWASM_RT_MODULE_PREFIX=%s' % cwriter.GetModulePrefix(i),
+ c_filename, cwd=out_dir)
+
+ if options.compile:
+ main_c = os.path.basename(main_filename)
+ main_exe = os.path.basename(utils.ChangeExt(json_file_path, ''))
+ args = ['-o', main_exe, main_c] + o_filenames + ['-lm']
+ cc.RunWithArgs(*args, cwd=out_dir)
+
+ if options.compile and options.run:
+ utils.Executable(os.path.join(out_dir, main_exe),
+ forward_stdout=True).RunWithArgs()
+
+ return 0
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(sys.argv[1:]))
+ except Error as e:
+ # TODO(binji): gcc will output unicode quotes in errors since the terminal
+ # environment allows it, but python2 stderr will always attempt to convert
+ # to ascii first, which fails. This will replace the invalid characters
+ # instead, which is ugly, but works.
+ sys.stderr.write(u'{0}\n'.format(e).encode('ascii', 'replace'))
+ sys.exit(1)
diff --git a/test/run-tests.py b/test/run-tests.py
index 630f3fe0..136ebeaf 100755
--- a/test/run-tests.py
+++ b/test/run-tests.py
@@ -141,8 +141,22 @@ TOOLS = {
],
'VERBOSE-FLAGS': ['--print-cmd', '-v']
},
+ 'run-spec-wasm2c': {
+ 'EXE': 'test/run-spec-wasm2c.py',
+ 'FLAGS': [
+ '--bindir=%(bindir)s',
+ '--no-error-cmdline',
+ '-o',
+ '%(out_dir)s',
+ ],
+ 'VERBOSE-FLAGS': ['--print-cmd', '-v']
+ }
}
+# TODO(binji): Add Windows support for compiling using run-spec-wasm2c.py
+if IS_WINDOWS:
+ TOOLS['run-spec-wasm2c']['SKIP'] = ''
+
ROUNDTRIP_TOOLS = ('wat2wasm',)
diff --git a/test/spec-wasm2c-prefix.c b/test/spec-wasm2c-prefix.c
new file mode 100644
index 00000000..b55664ce
--- /dev/null
+++ b/test/spec-wasm2c-prefix.c
@@ -0,0 +1,323 @@
+#include <assert.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <math.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PAGE_SIZE 65536
+
+typedef struct FuncType {
+ wasm_rt_type_t* params;
+ wasm_rt_type_t* results;
+ u32 param_count;
+ u32 result_count;
+} FuncType;
+
+int g_tests_run;
+int g_tests_passed;
+jmp_buf g_jmp_buf;
+FuncType* g_func_types;
+u32 g_func_type_count;
+
+static void run_spec_tests(void);
+
+static void error(const char* file, int line, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ fprintf(stderr, "%s:%d: assertion failed: ", file, line);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+#define ASSERT_TRAP(f) \
+ do { \
+ g_tests_run++; \
+ if (setjmp(g_jmp_buf) != 0) { \
+ g_tests_passed++; \
+ } else { \
+ (void)(f); \
+ error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \
+ } \
+ } while (0)
+
+#define ASSERT_EXHAUSTION(f) \
+ do { \
+ g_tests_run++; \
+ wasm_rt_trap_t code = setjmp(g_jmp_buf); \
+ switch (code) { \
+ case WASM_RT_TRAP_NONE: \
+ (void)(f); \
+ error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \
+ break; \
+ case WASM_RT_TRAP_EXHAUSTION: \
+ g_tests_passed++; \
+ break; \
+ default: \
+ error(__FILE__, __LINE__, \
+ "expected " #f \
+ " to trap due to exhaustion, got trap code %d.\n", \
+ code); \
+ break; \
+ } \
+ } while (0)
+
+#define ASSERT_RETURN(f) \
+ do { \
+ g_tests_run++; \
+ if (setjmp(g_jmp_buf) != 0) { \
+ error(__FILE__, __LINE__, #f " trapped.\n"); \
+ } else { \
+ f; \
+ g_tests_passed++; \
+ } \
+ } while (0)
+
+#define ASSERT_RETURN_T(type, fmt, f, expected) \
+ do { \
+ g_tests_run++; \
+ if (setjmp(g_jmp_buf) != 0) { \
+ error(__FILE__, __LINE__, #f " trapped.\n"); \
+ } else { \
+ type actual = f; \
+ if (is_equal_##type(actual, expected)) { \
+ g_tests_passed++; \
+ } else { \
+ error(__FILE__, __LINE__, \
+ "in " #f ": expected %" fmt ", got %" fmt ".\n", expected, \
+ actual); \
+ } \
+ } \
+ } while (0)
+
+#define ASSERT_RETURN_NAN_T(type, itype, fmt, f, kind) \
+ do { \
+ g_tests_run++; \
+ if (setjmp(g_jmp_buf) != 0) { \
+ error(__FILE__, __LINE__, #f " trapped.\n"); \
+ } else { \
+ type actual = f; \
+ itype iactual; \
+ memcpy(&iactual, &actual, sizeof(iactual)); \
+ if (is_##kind##_nan_##type(iactual)) { \
+ g_tests_passed++; \
+ } else { \
+ error(__FILE__, __LINE__, \
+ "in " #f ": expected result to be a " #kind " nan, got 0x%" fmt \
+ ".\n", \
+ iactual); \
+ } \
+ } \
+ } while (0)
+
+#define ASSERT_RETURN_I32(f, expected) ASSERT_RETURN_T(u32, "u", f, expected)
+#define ASSERT_RETURN_I64(f, expected) ASSERT_RETURN_T(u64, PRIu64, f, expected)
+#define ASSERT_RETURN_F32(f, expected) ASSERT_RETURN_T(f32, ".9g", f, expected)
+#define ASSERT_RETURN_F64(f, expected) ASSERT_RETURN_T(f64, ".17g", f, expected)
+
+#define ASSERT_RETURN_CANONICAL_NAN_F32(f) \
+ ASSERT_RETURN_NAN_T(f32, u32, "08x", f, canonical)
+#define ASSERT_RETURN_CANONICAL_NAN_F64(f) \
+ ASSERT_RETURN_NAN_T(f64, u64, "016x", f, canonical)
+#define ASSERT_RETURN_ARITHMETIC_NAN_F32(f) \
+ ASSERT_RETURN_NAN_T(f32, u32, "08x", f, arithmetic)
+#define ASSERT_RETURN_ARITHMETIC_NAN_F64(f) \
+ ASSERT_RETURN_NAN_T(f64, u64, "016x", f, arithmetic)
+
+static bool is_equal_u32(u32 x, u32 y) {
+ return x == y;
+}
+
+static bool is_equal_u64(u64 x, u64 y) {
+ return x == y;
+}
+
+static bool is_equal_f32(f32 x, f32 y) {
+ u32 ux, uy;
+ memcpy(&ux, &x, sizeof(ux));
+ memcpy(&uy, &y, sizeof(uy));
+ return ux == uy;
+}
+
+static bool is_equal_f64(f64 x, f64 y) {
+ u64 ux, uy;
+ memcpy(&ux, &x, sizeof(ux));
+ memcpy(&uy, &y, sizeof(uy));
+ return ux == uy;
+}
+
+static f32 make_nan_f32(u32 x) {
+ x |= 0x7f800000;
+ f32 res;
+ memcpy(&res, &x, sizeof(res));
+ return res;
+}
+
+static f64 make_nan_f64(u64 x) {
+ x |= 0x7ff0000000000000;
+ f64 res;
+ memcpy(&res, &x, sizeof(res));
+ return res;
+}
+
+static bool is_canonical_nan_f32(u32 x) {
+ return (x & 0x7fffffff) == 0x7fc00000;
+}
+
+static bool is_canonical_nan_f64(u64 x) {
+ return (x & 0x7fffffffffffffff) == 0x7ff8000000000000;
+}
+
+static bool is_arithmetic_nan_f32(u32 x) {
+ return (x & 0x7fc00000) == 0x7fc00000;
+}
+
+static bool is_arithmetic_nan_f64(u64 x) {
+ return (x & 0x7ff8000000000000) == 0x7ff8000000000000;
+}
+
+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;
+ int 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;
+}
+
+
+/*
+ * wasm_rt_* implementations
+ */
+
+uint32_t wasm_rt_call_stack_depth;
+
+void wasm_rt_trap(wasm_rt_trap_t code) {
+ assert(code != WASM_RT_TRAP_NONE);
+ longjmp(g_jmp_buf, code);
+}
+
+u32 wasm_rt_register_func_type(u32 param_count, u32 result_count, ...) {
+ FuncType func_type;
+ func_type.param_count = param_count;
+ func_type.params = malloc(param_count * sizeof(wasm_rt_type_t));
+ func_type.result_count = result_count;
+ func_type.results = malloc(result_count * sizeof(wasm_rt_type_t));
+
+ va_list args;
+ va_start(args, result_count);
+
+ u32 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)) {
+ free(func_type.params);
+ free(func_type.results);
+ return i + 1;
+ }
+ }
+
+ u32 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_allocate_memory(wasm_rt_memory_t* memory,
+ u32 initial_pages,
+ u32 max_pages) {
+ memory->pages = initial_pages;
+ memory->max_pages = max_pages;
+ memory->size = initial_pages * PAGE_SIZE;
+ memory->data = calloc(memory->size, 1);
+}
+
+u32 wasm_rt_grow_memory(wasm_rt_memory_t* memory, u32 delta) {
+ u32 old_pages = memory->pages;
+ u32 new_pages = memory->pages + delta;
+ if (new_pages < old_pages || new_pages > memory->max_pages) {
+ return (u32)-1;
+ }
+ memory->data = realloc(memory->data, new_pages);
+ memory->pages = new_pages;
+ memory->size = new_pages * PAGE_SIZE;
+ return old_pages;
+}
+
+void wasm_rt_allocate_table(wasm_rt_table_t* table,
+ u32 elements,
+ u32 max_elements) {
+ table->size = elements;
+ table->max_size = max_elements;
+ table->data = calloc(table->size, sizeof(wasm_rt_elem_t));
+}
+
+
+/*
+ * spectest implementations
+ */
+static void spectest_print_vv(void) {
+ printf("spectest.print()\n");
+}
+
+static void spectest_print_vi(uint32_t i) {
+ printf("spectest.print(%d)\n", i);
+}
+
+static void spectest_print_vf(float f) {
+ printf("spectest.print(%g)\n", f);
+}
+
+static void spectest_print_vif(uint32_t i, float f) {
+ printf("spectest.print(%d %g)\n", i, f);
+}
+
+static void spectest_print_vd(double d) {
+ printf("spectest.print(%g)\n", d);
+}
+
+static void spectest_print_vdd(double d1, double d2) {
+ printf("spectest.print(%g %g)\n", d1, d2);
+}
+
+static wasm_rt_table_t spectest_table;
+static wasm_rt_memory_t spectest_memory;
+static uint32_t spectest_global = 666;
+
+void (*Z_spectestZ_printZ_vv)(void) = &spectest_print_vv;
+void (*Z_spectestZ_printZ_vi)(uint32_t) = &spectest_print_vi;
+void (*Z_spectestZ_printZ_vf)(float) = &spectest_print_vf;
+void (*Z_spectestZ_printZ_vif)(uint32_t, float) = &spectest_print_vif;
+void (*Z_spectestZ_printZ_vd)(double) = &spectest_print_vd;
+void (*Z_spectestZ_printZ_vdd)(double, double) = &spectest_print_vdd;
+wasm_rt_table_t* Z_spectestZ_table = &spectest_table;
+wasm_rt_memory_t* Z_spectestZ_memory = &spectest_memory;
+uint32_t* Z_spectestZ_globalZ_i = &spectest_global;
+
+static void init_spectest_module(void) {
+ wasm_rt_allocate_memory(&spectest_memory, 1, 2);
+ wasm_rt_allocate_table(&spectest_table, 10, 20);
+}
+
+
+int main(int argc, char** argv) {
+ init_spectest_module();
+ run_spec_tests();
+ printf("%u/%u tests passed.\n", g_tests_passed, g_tests_run);
+ return g_tests_passed != g_tests_run;
+}
+
diff --git a/test/utils.py b/test/utils.py
index d2bf1c2c..00ece562 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -46,8 +46,13 @@ class Executable(object):
self.error_cmdline = kwargs.get('error_cmdline', True)
self.clean_stdout = kwargs.get('clean_stdout')
self.clean_stderr = kwargs.get('clean_stderr')
+ self.stdout_handle = self._ForwardHandle(kwargs.get('forward_stdout'))
+ self.stderr_handle = self._ForwardHandle(kwargs.get('forward_stderr'))
self.verbose = False
+ def _ForwardHandle(self, forward):
+ return None if forward else subprocess.PIPE
+
def _RunWithArgsInternal(self, *args, **kwargs):
cmd = [self.exe] + self.before_args + list(args) + self.after_args
cmd_str = ' '.join(cmd)
@@ -63,11 +68,13 @@ class Executable(object):
stderr = ''
error = None
try:
- process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, **kwargs)
+ process = subprocess.Popen(cmd, stdout=self.stdout_handle,
+ stderr=self.stderr_handle, **kwargs)
stdout, stderr = process.communicate()
- stdout = stdout.decode('utf-8', 'ignore')
- stderr = stderr.decode('utf-8', 'ignore')
+ if stdout:
+ stdout = stdout.decode('utf-8', 'ignore')
+ if stderr:
+ stderr = stderr.decode('utf-8', 'ignore')
if self.clean_stdout:
stdout = self.clean_stdout(stdout)
if self.clean_stderr:
@@ -91,7 +98,8 @@ class Executable(object):
def RunWithArgs(self, *args, **kwargs):
stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs)
- sys.stdout.write(stdout)
+ if stdout:
+ sys.stdout.write(stdout)
if error:
raise error
diff --git a/test/wasm2c/spec/address.txt b/test/wasm2c/spec/address.txt
new file mode 100644
index 00000000..dd835229
--- /dev/null
+++ b/test/wasm2c/spec/address.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/address.wast
+(;; STDOUT ;;;
+41/41 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/align.txt b/test/wasm2c/spec/align.txt
new file mode 100644
index 00000000..5e474cec
--- /dev/null
+++ b/test/wasm2c/spec/align.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/align.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/binary.txt b/test/wasm2c/spec/binary.txt
new file mode 100644
index 00000000..19ea66af
--- /dev/null
+++ b/test/wasm2c/spec/binary.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/binary.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/block.txt b/test/wasm2c/spec/block.txt
new file mode 100644
index 00000000..c76674f6
--- /dev/null
+++ b/test/wasm2c/spec/block.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/block.wast
+(;; STDOUT ;;;
+14/14 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/br.txt b/test/wasm2c/spec/br.txt
new file mode 100644
index 00000000..e527a6e0
--- /dev/null
+++ b/test/wasm2c/spec/br.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/br.wast
+(;; STDOUT ;;;
+61/61 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/br_if.txt b/test/wasm2c/spec/br_if.txt
new file mode 100644
index 00000000..e00c1d26
--- /dev/null
+++ b/test/wasm2c/spec/br_if.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/br_if.wast
+(;; STDOUT ;;;
+34/34 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/br_table.txt b/test/wasm2c/spec/br_table.txt
new file mode 100644
index 00000000..4291831a
--- /dev/null
+++ b/test/wasm2c/spec/br_table.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/br_table.wast
+(;; STDOUT ;;;
+144/144 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/break-drop.txt b/test/wasm2c/spec/break-drop.txt
new file mode 100644
index 00000000..bdeaafa8
--- /dev/null
+++ b/test/wasm2c/spec/break-drop.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/break-drop.wast
+(;; STDOUT ;;;
+3/3 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/call.txt b/test/wasm2c/spec/call.txt
new file mode 100644
index 00000000..2c73b2d7
--- /dev/null
+++ b/test/wasm2c/spec/call.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/call.wast
+(;; STDOUT ;;;
+35/35 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/call_indirect.txt b/test/wasm2c/spec/call_indirect.txt
new file mode 100644
index 00000000..352a5a74
--- /dev/null
+++ b/test/wasm2c/spec/call_indirect.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/call_indirect.wast
+(;; STDOUT ;;;
+48/48 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/comments.txt b/test/wasm2c/spec/comments.txt
new file mode 100644
index 00000000..03a29828
--- /dev/null
+++ b/test/wasm2c/spec/comments.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/comments.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/const.txt b/test/wasm2c/spec/const.txt
new file mode 100644
index 00000000..c5efaceb
--- /dev/null
+++ b/test/wasm2c/spec/const.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/const.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/conversions.txt b/test/wasm2c/spec/conversions.txt
new file mode 100644
index 00000000..437bf4dd
--- /dev/null
+++ b/test/wasm2c/spec/conversions.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/conversions.wast
+(;; STDOUT ;;;
+401/401 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/custom_section.txt b/test/wasm2c/spec/custom_section.txt
new file mode 100644
index 00000000..504c42a2
--- /dev/null
+++ b/test/wasm2c/spec/custom_section.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/custom_section.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/endianness.txt b/test/wasm2c/spec/endianness.txt
new file mode 100644
index 00000000..8a2dda76
--- /dev/null
+++ b/test/wasm2c/spec/endianness.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/endianness.wast
+(;; STDOUT ;;;
+68/68 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/exports.txt b/test/wasm2c/spec/exports.txt
new file mode 100644
index 00000000..8fc35fd2
--- /dev/null
+++ b/test/wasm2c/spec/exports.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/exports.wast
+(;; STDOUT ;;;
+6/6 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f32.txt b/test/wasm2c/spec/f32.txt
new file mode 100644
index 00000000..e621cbad
--- /dev/null
+++ b/test/wasm2c/spec/f32.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f32.wast
+(;; STDOUT ;;;
+2500/2500 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f32_bitwise.txt b/test/wasm2c/spec/f32_bitwise.txt
new file mode 100644
index 00000000..413467c0
--- /dev/null
+++ b/test/wasm2c/spec/f32_bitwise.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f32_bitwise.wast
+(;; STDOUT ;;;
+360/360 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f32_cmp.txt b/test/wasm2c/spec/f32_cmp.txt
new file mode 100644
index 00000000..e9ffa524
--- /dev/null
+++ b/test/wasm2c/spec/f32_cmp.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f32_cmp.wast
+(;; STDOUT ;;;
+2400/2400 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f64.txt b/test/wasm2c/spec/f64.txt
new file mode 100644
index 00000000..6b5b309f
--- /dev/null
+++ b/test/wasm2c/spec/f64.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f64.wast
+(;; STDOUT ;;;
+2500/2500 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f64_bitwise.txt b/test/wasm2c/spec/f64_bitwise.txt
new file mode 100644
index 00000000..738ebd75
--- /dev/null
+++ b/test/wasm2c/spec/f64_bitwise.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f64_bitwise.wast
+(;; STDOUT ;;;
+360/360 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/f64_cmp.txt b/test/wasm2c/spec/f64_cmp.txt
new file mode 100644
index 00000000..dce7125f
--- /dev/null
+++ b/test/wasm2c/spec/f64_cmp.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/f64_cmp.wast
+(;; STDOUT ;;;
+2400/2400 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/fac.txt b/test/wasm2c/spec/fac.txt
new file mode 100644
index 00000000..eeb0a11f
--- /dev/null
+++ b/test/wasm2c/spec/fac.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/fac.wast
+(;; STDOUT ;;;
+6/6 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/float_exprs.txt b/test/wasm2c/spec/float_exprs.txt
new file mode 100644
index 00000000..e0f92791
--- /dev/null
+++ b/test/wasm2c/spec/float_exprs.txt
@@ -0,0 +1,6 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/float_exprs.wast
+(;; STDOUT ;;;
+794/794 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/float_literals.txt b/test/wasm2c/spec/float_literals.txt
new file mode 100644
index 00000000..5eea5d86
--- /dev/null
+++ b/test/wasm2c/spec/float_literals.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/float_literals.wast
+(;; STDOUT ;;;
+81/81 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/float_memory.txt b/test/wasm2c/spec/float_memory.txt
new file mode 100644
index 00000000..68a408e7
--- /dev/null
+++ b/test/wasm2c/spec/float_memory.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/float_memory.wast
+(;; STDOUT ;;;
+60/60 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/float_misc.txt b/test/wasm2c/spec/float_misc.txt
new file mode 100644
index 00000000..f7f9b23c
--- /dev/null
+++ b/test/wasm2c/spec/float_misc.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/float_misc.wast
+(;; STDOUT ;;;
+440/440 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/forward.txt b/test/wasm2c/spec/forward.txt
new file mode 100644
index 00000000..47f17a46
--- /dev/null
+++ b/test/wasm2c/spec/forward.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/forward.wast
+(;; STDOUT ;;;
+4/4 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/func.txt b/test/wasm2c/spec/func.txt
new file mode 100644
index 00000000..9a9e5e71
--- /dev/null
+++ b/test/wasm2c/spec/func.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/func.wast
+(;; STDOUT ;;;
+73/73 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/func_ptrs.txt b/test/wasm2c/spec/func_ptrs.txt
new file mode 100644
index 00000000..2f7a8ffe
--- /dev/null
+++ b/test/wasm2c/spec/func_ptrs.txt
@@ -0,0 +1,6 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/func_ptrs.wast
+(;; STDOUT ;;;
+spectest.print(83)
+25/25 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/get_local.txt b/test/wasm2c/spec/get_local.txt
new file mode 100644
index 00000000..223eb428
--- /dev/null
+++ b/test/wasm2c/spec/get_local.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/get_local.wast
+(;; STDOUT ;;;
+10/10 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/globals.txt b/test/wasm2c/spec/globals.txt
new file mode 100644
index 00000000..a9535120
--- /dev/null
+++ b/test/wasm2c/spec/globals.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/globals.wast
+(;; STDOUT ;;;
+16/16 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/i32.txt b/test/wasm2c/spec/i32.txt
new file mode 100644
index 00000000..24fa75de
--- /dev/null
+++ b/test/wasm2c/spec/i32.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/i32.wast
+(;; STDOUT ;;;
+359/359 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/i64.txt b/test/wasm2c/spec/i64.txt
new file mode 100644
index 00000000..e9673980
--- /dev/null
+++ b/test/wasm2c/spec/i64.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/i64.wast
+(;; STDOUT ;;;
+359/359 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/if.txt b/test/wasm2c/spec/if.txt
new file mode 100644
index 00000000..15aeb50b
--- /dev/null
+++ b/test/wasm2c/spec/if.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/if.wast
+(;; STDOUT ;;;
+38/38 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/imports.txt b/test/wasm2c/spec/imports.txt
new file mode 100644
index 00000000..77761421
--- /dev/null
+++ b/test/wasm2c/spec/imports.txt
@@ -0,0 +1,16 @@
+;;; SLOW:
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/imports.wast
+(;; STDOUT ;;;
+spectest.print(13)
+spectest.print(14 42)
+spectest.print(13)
+spectest.print(13)
+spectest.print(13)
+spectest.print(13)
+spectest.print(25 53)
+spectest.print(24)
+spectest.print(24)
+spectest.print(24)
+29/29 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/inline-module.txt b/test/wasm2c/spec/inline-module.txt
new file mode 100644
index 00000000..f37e1ebd
--- /dev/null
+++ b/test/wasm2c/spec/inline-module.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/inline-module.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/int_exprs.txt b/test/wasm2c/spec/int_exprs.txt
new file mode 100644
index 00000000..239aee75
--- /dev/null
+++ b/test/wasm2c/spec/int_exprs.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/int_exprs.wast
+(;; STDOUT ;;;
+89/89 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/int_literals.txt b/test/wasm2c/spec/int_literals.txt
new file mode 100644
index 00000000..b4e176a0
--- /dev/null
+++ b/test/wasm2c/spec/int_literals.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/int_literals.wast
+(;; STDOUT ;;;
+30/30 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/labels.txt b/test/wasm2c/spec/labels.txt
new file mode 100644
index 00000000..6292bb8b
--- /dev/null
+++ b/test/wasm2c/spec/labels.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/labels.wast
+(;; STDOUT ;;;
+24/24 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/left-to-right.txt b/test/wasm2c/spec/left-to-right.txt
new file mode 100644
index 00000000..a4348aed
--- /dev/null
+++ b/test/wasm2c/spec/left-to-right.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/left-to-right.wast
+(;; STDOUT ;;;
+95/95 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/linking.txt b/test/wasm2c/spec/linking.txt
new file mode 100644
index 00000000..2c420fc9
--- /dev/null
+++ b/test/wasm2c/spec/linking.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/linking.wast
+(;; STDOUT ;;;
+70/70 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/loop.txt b/test/wasm2c/spec/loop.txt
new file mode 100644
index 00000000..804df39d
--- /dev/null
+++ b/test/wasm2c/spec/loop.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/loop.wast
+(;; STDOUT ;;;
+42/42 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/memory.txt b/test/wasm2c/spec/memory.txt
new file mode 100644
index 00000000..4ee65bfe
--- /dev/null
+++ b/test/wasm2c/spec/memory.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/memory.wast
+(;; STDOUT ;;;
+47/47 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/memory_redundancy.txt b/test/wasm2c/spec/memory_redundancy.txt
new file mode 100644
index 00000000..9fad9cb5
--- /dev/null
+++ b/test/wasm2c/spec/memory_redundancy.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/memory_redundancy.wast
+(;; STDOUT ;;;
+4/4 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/memory_trap.txt b/test/wasm2c/spec/memory_trap.txt
new file mode 100644
index 00000000..afecd5e9
--- /dev/null
+++ b/test/wasm2c/spec/memory_trap.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/memory_trap.wast
+(;; STDOUT ;;;
+171/171 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/names.txt b/test/wasm2c/spec/names.txt
new file mode 100644
index 00000000..b35478c6
--- /dev/null
+++ b/test/wasm2c/spec/names.txt
@@ -0,0 +1,7 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/names.wast
+(;; STDOUT ;;;
+spectest.print(42)
+spectest.print(123)
+475/475 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/nop.txt b/test/wasm2c/spec/nop.txt
new file mode 100644
index 00000000..5c04da66
--- /dev/null
+++ b/test/wasm2c/spec/nop.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/nop.wast
+(;; STDOUT ;;;
+50/50 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/resizing.txt b/test/wasm2c/spec/resizing.txt
new file mode 100644
index 00000000..05d35303
--- /dev/null
+++ b/test/wasm2c/spec/resizing.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/resizing.wast
+(;; STDOUT ;;;
+34/34 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/return.txt b/test/wasm2c/spec/return.txt
new file mode 100644
index 00000000..6fe61a0f
--- /dev/null
+++ b/test/wasm2c/spec/return.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/return.wast
+(;; STDOUT ;;;
+57/57 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/select.txt b/test/wasm2c/spec/select.txt
new file mode 100644
index 00000000..5617bcb6
--- /dev/null
+++ b/test/wasm2c/spec/select.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/select.wast
+(;; STDOUT ;;;
+28/28 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/set_local.txt b/test/wasm2c/spec/set_local.txt
new file mode 100644
index 00000000..928d75d2
--- /dev/null
+++ b/test/wasm2c/spec/set_local.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/set_local.wast
+(;; STDOUT ;;;
+10/10 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/skip-stack-guard-page.txt b/test/wasm2c/spec/skip-stack-guard-page.txt
new file mode 100644
index 00000000..171ae261
--- /dev/null
+++ b/test/wasm2c/spec/skip-stack-guard-page.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/skip-stack-guard-page.wast
+(;; STDOUT ;;;
+10/10 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/stack.txt b/test/wasm2c/spec/stack.txt
new file mode 100644
index 00000000..128520d1
--- /dev/null
+++ b/test/wasm2c/spec/stack.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/stack.wast
+(;; STDOUT ;;;
+3/3 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/start.txt b/test/wasm2c/spec/start.txt
new file mode 100644
index 00000000..3eb90c43
--- /dev/null
+++ b/test/wasm2c/spec/start.txt
@@ -0,0 +1,8 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/start.wast
+(;; STDOUT ;;;
+spectest.print(1)
+spectest.print(2)
+spectest.print()
+6/6 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/store_retval.txt b/test/wasm2c/spec/store_retval.txt
new file mode 100644
index 00000000..94eb4481
--- /dev/null
+++ b/test/wasm2c/spec/store_retval.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/store_retval.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/switch.txt b/test/wasm2c/spec/switch.txt
new file mode 100644
index 00000000..5504097f
--- /dev/null
+++ b/test/wasm2c/spec/switch.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/switch.wast
+(;; STDOUT ;;;
+26/26 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/tee_local.txt b/test/wasm2c/spec/tee_local.txt
new file mode 100644
index 00000000..74c0ec81
--- /dev/null
+++ b/test/wasm2c/spec/tee_local.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/tee_local.wast
+(;; STDOUT ;;;
+11/11 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/token.txt b/test/wasm2c/spec/token.txt
new file mode 100644
index 00000000..d42b0828
--- /dev/null
+++ b/test/wasm2c/spec/token.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/token.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/traps.txt b/test/wasm2c/spec/traps.txt
new file mode 100644
index 00000000..e8ae54d4
--- /dev/null
+++ b/test/wasm2c/spec/traps.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/traps.wast
+(;; STDOUT ;;;
+32/32 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/type.txt b/test/wasm2c/spec/type.txt
new file mode 100644
index 00000000..c054bda0
--- /dev/null
+++ b/test/wasm2c/spec/type.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/type.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/typecheck.txt b/test/wasm2c/spec/typecheck.txt
new file mode 100644
index 00000000..8346a9e5
--- /dev/null
+++ b/test/wasm2c/spec/typecheck.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/typecheck.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/unreachable.txt b/test/wasm2c/spec/unreachable.txt
new file mode 100644
index 00000000..cd1f3d70
--- /dev/null
+++ b/test/wasm2c/spec/unreachable.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/unreachable.wast
+(;; STDOUT ;;;
+59/59 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/unreached-invalid.txt b/test/wasm2c/spec/unreached-invalid.txt
new file mode 100644
index 00000000..ca6f3718
--- /dev/null
+++ b/test/wasm2c/spec/unreached-invalid.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/unreached-invalid.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/unwind.txt b/test/wasm2c/spec/unwind.txt
new file mode 100644
index 00000000..11c16763
--- /dev/null
+++ b/test/wasm2c/spec/unwind.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/unwind.wast
+(;; STDOUT ;;;
+38/38 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/utf8-custom-section-id.txt b/test/wasm2c/spec/utf8-custom-section-id.txt
new file mode 100644
index 00000000..8d21894c
--- /dev/null
+++ b/test/wasm2c/spec/utf8-custom-section-id.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/utf8-custom-section-id.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/utf8-import-field.txt b/test/wasm2c/spec/utf8-import-field.txt
new file mode 100644
index 00000000..8a4e230a
--- /dev/null
+++ b/test/wasm2c/spec/utf8-import-field.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/utf8-import-field.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/utf8-import-module.txt b/test/wasm2c/spec/utf8-import-module.txt
new file mode 100644
index 00000000..080ec16c
--- /dev/null
+++ b/test/wasm2c/spec/utf8-import-module.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/utf8-import-module.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)
diff --git a/test/wasm2c/spec/utf8-invalid-encoding.txt b/test/wasm2c/spec/utf8-invalid-encoding.txt
new file mode 100644
index 00000000..9b42324f
--- /dev/null
+++ b/test/wasm2c/spec/utf8-invalid-encoding.txt
@@ -0,0 +1,5 @@
+;;; TOOL: run-spec-wasm2c
+;;; STDIN_FILE: third_party/testsuite/utf8-invalid-encoding.wast
+(;; STDOUT ;;;
+0/0 tests passed.
+;;; STDOUT ;;)