diff options
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) @@ -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 = ⌖ + +/* 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_ */ @@ -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()) { @@ -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 ;;) |