diff --git a/.appveyor.yml b/.appveyor.yml index b07300e7d..c8655d67e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,23 +15,31 @@ environment: matrix: - GENERATOR: MSYS Makefiles CONFIG: Release + TARGET: x86 JOBS_FLAG: -j + MSVC_FLAG: EXE_DIR: . DEPLOY: false - GENERATOR: Visual Studio 14 2015 CONFIG: Release + TARGET: x86 JOBS_FLAG: "/m:" + MSVC_FLAG: --msvc EXE_DIR: '%CONFIG%' DEPLOY: true DEPLOY_NAME: wabt-%APPVEYOR_REPO_TAG_NAME%-win32.zip - GENERATOR: Visual Studio 14 2015 Win64 CONFIG: Debug + TARGET: x86_amd64 JOBS_FLAG: "/m:" + MSVC_FLAG: --msvc EXE_DIR: '%CONFIG%' DEPLOY: false - GENERATOR: Visual Studio 14 2015 Win64 CONFIG: Release + TARGET: x86_amd64 JOBS_FLAG: "/m:" + MSVC_FLAG: --msvc EXE_DIR: '%CONFIG%' DEPLOY: true DEPLOY_NAME: wabt-%APPVEYOR_REPO_TAG_NAME%-win64.zip @@ -41,7 +49,7 @@ build_script: - cmake --build . --config %CONFIG% --target install -- %JOBS_FLAG%%JOBS% test_script: - - python test\run-tests.py -v --bindir %APPVEYOR_BUILD_FOLDER%\bin + - call scripts\appveyor-test-script.bat # Must happen before artifacts step. after_test: diff --git a/scripts/appveyor-test-script.bat b/scripts/appveyor-test-script.bat new file mode 100644 index 000000000..b6965cfe9 --- /dev/null +++ b/scripts/appveyor-test-script.bat @@ -0,0 +1,29 @@ +REM Copyright 2018 WebAssembly Community Group participants +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +REM Run unittests +%APPVEYOR_BUILD_FOLDER%\bin\wabt-unittests + +REM Set up environment so cl.exe is in the path. +REM See docs here: https://www.appveyor.com/docs/lang/cpp/#visual-studio-2015 + +IF "%TARGET%" == "x86" ( + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 +) ELSE ( + call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 +) + +REM Run tests +python test\run-tests.py -v --bindir %APPVEYOR_BUILD_FOLDER%\bin %MSVC_FLAG% diff --git a/src/c-writer.cc b/src/c-writer.cc index 0ba530e0a..453913995 100644 --- a/src/c-writer.cc +++ b/src/c-writer.cc @@ -170,6 +170,7 @@ class CWriter { static std::string MangleGlobalName(string_view, Type); static std::string LegalizeName(string_view); static std::string ExportName(string_view mangled_name); + static std::string ExportFuncName(string_view mangled_name); std::string DefineName(SymbolSet*, string_view); std::string DefineImportName(const std::string& name, string_view module_name, @@ -178,6 +179,8 @@ class CWriter { std::string DefineLocalScopeName(const std::string&); std::string DefineStackVarName(Index, Type, string_view); + size_t GetStackFrameSize(const Func&); + void Indent(int size = INDENT_SIZE); void Dedent(int size = INDENT_SIZE); void WriteIndent(); @@ -344,36 +347,38 @@ static const char* s_global_symbols[] = { "strlen", "strncat", "strncmp", "strncpy", "strpbrk", "strrchr", "strspn", "strstr", "strtok", "strxfrm", + // MSVC macros + "min", "max", + // 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", - + "DIVREM_U", "DIV_S", "DIV_U", "f32", "F32_ABS", "F64_ABS", "F32_COPYSIGN", + "F64_COPYSIGN", "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_CC", "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", }; #define SECTION_NAME(x) s_header_##x @@ -490,14 +495,23 @@ std::string CWriter::MangleTypes(const TypeVector& types) { return result; } +// Use unsigned char instead of char to avoid undefined behavior. +bool IsAlpha(unsigned char c) { + return isalpha(c); +} + +bool IsAlnum(unsigned char c) { + return isalnum(c); +} + // 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 == '_') { + for (unsigned char c : name) { + if ((IsAlnum(c) && c != kPrefix) || c == '_') { result += c; } else { result += kPrefix; @@ -534,9 +548,9 @@ std::string CWriter::LegalizeName(string_view name) { return "_"; std::string result; - result = isalpha(name[0]) ? name[0] : '_'; + result = IsAlpha(name[0]) ? name[0] : '_'; for (size_t i = 1; i < name.size(); ++i) - result += isalnum(name[i]) ? name[i] : '_'; + result += IsAlnum(name[i]) ? name[i] : '_'; return result; } @@ -568,7 +582,7 @@ std::string CWriter::DefineImportName(const std::string& name, import_syms_.insert(name); global_syms_.insert(mangled); global_sym_map_.insert(SymbolMap::value_type(name, mangled)); - return "(*" + mangled + ")"; + return mangled; } std::string CWriter::DefineGlobalScopeName(const std::string& name) { @@ -592,6 +606,28 @@ std::string CWriter::DefineStackVarName(Index index, return unique; } +size_t TypeSizeInBytes(Type type) { + switch (type) { + case Type::I32: return 4; + case Type::I64: return 8; + case Type::F32: return 4; + case Type::F64: return 8; + case Type::V128: return 16; + default: return 0; + } +} + +size_t CWriter::GetStackFrameSize(const Func& func) { + size_t total = 0; + for (Type param_type : func.decl.sig.param_types) { + total += TypeSizeInBytes(param_type); + } + for (Type local_type : func.local_types) { + total += TypeSizeInBytes(local_type); + } + return total; +} + void CWriter::Indent(int size) { indent_ += size; } @@ -802,11 +838,8 @@ void CWriter::Write(const Const& const_) { 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(const_.f32_bits)); + Writef("%1.9ef", Bitcast(const_.f32_bits)); } break; } @@ -825,11 +858,8 @@ void CWriter::Write(const Const& const_) { " */", 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(const_.f64_bits)); + Writef("%1.17e", Bitcast(const_.f64_bits)); } break; @@ -867,7 +897,7 @@ void CWriter::InitGlobalSymbols() { std::string CWriter::GenerateHeaderGuard() const { std::string result; for (char c : header_name_) { - if (isalnum(c) || c == '_') { + if (IsAlnum(c) || c == '_') { result += toupper(c); } else { result += '_'; @@ -885,8 +915,10 @@ void CWriter::WriteSourceTop() { void CWriter::WriteFuncTypes() { Write(Newline()); - Writef("static u32 func_types[%" PRIzd "];", module_->func_types.size()); - Write(Newline(), Newline()); + if (!module_->func_types.empty()) { + 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) { @@ -922,37 +954,38 @@ void CWriter::WriteImports() { switch (import->kind()) { case ExternalKind::Func: { const Func& func = cast(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))); + std::string mangled_name = DefineImportName( + func.name, import->module_name, + MangleFuncName(import->field_name, func.decl.sig.param_types, + func.decl.sig.result_types)); + WriteFuncDeclaration(func.decl, "(WASM_RT_CC * " + mangled_name + ")"); Write(";"); break; } case ExternalKind::Global: { const Global& global = cast(import)->global; - WriteGlobal(global, - DefineImportName( - global.name, import->module_name, - MangleGlobalName(import->field_name, global.type))); + std::string mangled_name = + DefineImportName(global.name, import->module_name, + MangleGlobalName(import->field_name, global.type)); + WriteGlobal(global, Deref(mangled_name)); Write(";"); break; } case ExternalKind::Memory: { const Memory& memory = cast(import)->memory; - WriteMemory(DefineImportName(memory.name, import->module_name, - MangleName(import->field_name))); + std::string mangled_name = DefineImportName( + memory.name, import->module_name, MangleName(import->field_name)); + WriteMemory(Deref(mangled_name)); break; } case ExternalKind::Table: { const Table& table = cast(import)->table; - WriteTable(DefineImportName(table.name, import->module_name, - MangleName(import->field_name))); + std::string mangled_name = DefineImportName( + table.name, import->module_name, MangleName(import->field_name)); + WriteTable(Deref(mangled_name)); break; } @@ -975,7 +1008,8 @@ void CWriter::WriteFuncDeclarations() { bool is_import = func_index < module_->num_func_imports; if (!is_import) { Write("static "); - WriteFuncDeclaration(func->decl, DefineGlobalScopeName(func->name)); + WriteFuncDeclaration(func->decl, + "WASM_RT_CC " + DefineGlobalScopeName(func->name)); Write(";", Newline()); } ++func_index; @@ -1087,6 +1121,10 @@ void CWriter::WriteDataInitializers() { Write(Newline()); } else { for (const DataSegment* data_segment : module_->data_segments) { + if (data_segment->data.empty()) { + continue; + } + Write(Newline(), "static const u8 data_segment_data_", data_segment_index, "[] = ", OpenBrace()); size_t i = 0; @@ -1114,6 +1152,10 @@ void CWriter::WriteDataInitializers() { } data_segment_index = 0; for (const DataSegment* data_segment : module_->data_segments) { + if (data_segment->data.empty()) { + continue; + } + Write("memcpy(&(", ExternalRef(memory->name), ".data["); WriteInitExpr(data_segment->offset); Write("]), data_segment_data_", data_segment_index, ", ", @@ -1188,7 +1230,8 @@ void CWriter::WriteExports(WriteExportsKind kind) { func->decl.sig.result_types)); internal_name = func->name; if (kind != WriteExportsKind::Initializers) { - WriteFuncDeclaration(func->decl, Deref(mangled_name)); + WriteFuncDeclaration(func->decl, + "(WASM_RT_CC * " + mangled_name + ")"); Write(";"); } break; @@ -1268,10 +1311,12 @@ void CWriter::Write(const Func& func) { local_sym_map_.clear(); stack_var_sym_map_.clear(); - Write("static ", ResultType(func.decl.sig.result_types), " ", + size_t stack_frame_size = GetStackFrameSize(func); + + Write("static ", ResultType(func.decl.sig.result_types), " WASM_RT_CC ", GlobalName(func.name), "("); WriteParamsAndLocals(); - Write("FUNC_PROLOGUE;", Newline()); + Write("FUNC_PROLOGUE(", stack_frame_size, ");", Newline()); stream_ = &func_stream_; stream_->ClearOffset(); @@ -1284,7 +1329,7 @@ void CWriter::Write(const Func& func) { PopLabel(); ResetTypeStack(0); PushTypes(func.decl.sig.result_types); - Write("FUNC_EPILOGUE;", Newline()); + Write("FUNC_EPILOGUE(", stack_frame_size, ");", Newline()); if (!func.decl.sig.result_types.empty()) { // Return the top of the stack implicitly. @@ -1474,7 +1519,7 @@ void CWriter::Write(const ExprList& exprs) { Index func_type_index = module_->GetFuncTypeIndex(decl.type_var); Write("CALL_INDIRECT(", ExternalRef(table->name), ", "); - WriteFuncDeclaration(decl, "(*)"); + WriteFuncDeclaration(decl, "(WASM_RT_CC *)"); Write(", ", func_type_index, ", ", StackVar(0)); for (Index i = 0; i < num_params; ++i) { Write(", ", StackVar(num_params - i)); @@ -1836,11 +1881,11 @@ void CWriter::Write(const BinaryExpr& expr) { break; case Opcode::F32Copysign: - WritePrefixBinaryExpr(expr.opcode, "copysignf"); + WritePrefixBinaryExpr(expr.opcode, "F32_COPYSIGN"); break; case Opcode::F64Copysign: - WritePrefixBinaryExpr(expr.opcode, "copysign"); + WritePrefixBinaryExpr(expr.opcode, "F64_COPYSIGN"); break; default: @@ -2065,10 +2110,10 @@ void CWriter::Write(const LoadExpr& expr) { Type result_type = expr.opcode.GetResultType(); Write(StackVar(0, result_type), " = ", func, "(", ExternalPtr(memory->name), - ", (u64)(", StackVar(0)); + ", ", StackVar(0)); if (expr.offset != 0) - Write(" + ", expr.offset); - Write("));", Newline()); + Write(" + (u64)", expr.offset); + Write(");", Newline()); DropTypes(1); PushType(result_type); } @@ -2093,10 +2138,10 @@ void CWriter::Write(const StoreExpr& expr) { assert(module_->memories.size() == 1); Memory* memory = module_->memories[0]; - Write(func, "(", ExternalPtr(memory->name), ", (u64)(", StackVar(1)); + Write(func, "(", ExternalPtr(memory->name), ", ", StackVar(1)); if (expr.offset != 0) - Write(" + ", expr.offset); - Write("), ", StackVar(0), ");", Newline()); + Write(" + (u64)", expr.offset); + Write(", ", StackVar(0), ");", Newline()); DropTypes(2); } @@ -2132,11 +2177,11 @@ void CWriter::Write(const UnaryExpr& expr) { break; case Opcode::F32Abs: - WriteSimpleUnaryExpr(expr.opcode, "fabsf"); + WriteSimpleUnaryExpr(expr.opcode, "F32_ABS"); break; case Opcode::F64Abs: - WriteSimpleUnaryExpr(expr.opcode, "fabs"); + WriteSimpleUnaryExpr(expr.opcode, "F64_ABS"); break; case Opcode::F32Sqrt: diff --git a/src/prebuilt/wasm2c.include.c b/src/prebuilt/wasm2c.include.c index b14cc4357..360fd88cf 100644 --- a/src/prebuilt/wasm2c.include.c +++ b/src/prebuilt/wasm2c.include.c @@ -2,19 +2,31 @@ const char SECTION_NAME(includes)[] = "#include \n" "#include \n" +"#if _MSC_VER\n" +"#include \n" +"#include \n" +"#endif\n" ; const char SECTION_NAME(declarations)[] = +"#if _MSC_VER\n" +"#define UNLIKELY(x) (x)\n" +"#define LIKELY(x) (x)\n" +"#else\n" "#define UNLIKELY(x) __builtin_expect(!!(x), 0)\n" "#define LIKELY(x) __builtin_expect(!!(x), 1)\n" +"#endif\n" "\n" "#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)\n" "\n" -"#define FUNC_PROLOGUE \\\n" -" if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \\\n" +"#define FUNC_PROLOGUE(frame_size) \\\n" +" if ((++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) || \\\n" +" ((wasm_rt_call_stack_size += frame_size) > WASM_RT_MAX_CALL_STACK_SIZE)) \\\n" " TRAP(EXHAUSTION)\n" "\n" -"#define FUNC_EPILOGUE --wasm_rt_call_stack_depth\n" +"#define FUNC_EPILOGUE(frame_size) \\\n" +" --wasm_rt_call_stack_depth; \\\n" +" wasm_rt_call_stack_size -= frame_size\n" "\n" "#define UNREACHABLE TRAP(UNREACHABLE)\n" "\n" @@ -27,19 +39,20 @@ const char SECTION_NAME(declarations)[] = "#define MEMCHECK(mem, a, t) \\\n" " if (UNLIKELY((a) + sizeof(t) > mem->size)) TRAP(OOB)\n" "\n" -"#define DEFINE_LOAD(name, t1, t2, t3) \\\n" -" static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \\\n" -" MEMCHECK(mem, addr, t1); \\\n" -" t1 result; \\\n" -" memcpy(&result, &mem->data[addr], sizeof(t1)); \\\n" -" return (t3)(t2)result; \\\n" +"#define DEFINE_LOAD(name, t1, t2, t3) \\\n" +" static inline t3 WASM_RT_CC name(wasm_rt_memory_t* mem, u64 addr) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 result; \\\n" +" memcpy(&result, &mem->data[addr], sizeof(t1)); \\\n" +" return (t3)(t2)result; \\\n" " }\n" "\n" -"#define DEFINE_STORE(name, t1, t2) \\\n" -" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \\\n" -" MEMCHECK(mem, addr, t1); \\\n" -" t1 wrapped = (t1)value; \\\n" -" memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \\\n" +"#define DEFINE_STORE(name, t1, t2) \\\n" +" static inline void WASM_RT_CC name(wasm_rt_memory_t* mem, u64 addr, \\\n" +" t2 value) { \\\n" +" MEMCHECK(mem, addr, t1); \\\n" +" t1 wrapped = (t1)value; \\\n" +" memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \\\n" " }\n" "\n" "DEFINE_LOAD(i32_load, u32, u32, u32);\n" @@ -66,12 +79,88 @@ const char SECTION_NAME(declarations)[] = "DEFINE_STORE(i64_store16, u16, u64);\n" "DEFINE_STORE(i64_store32, u32, u64);\n" "\n" +"#if _MSC_VER\n" +"#if _M_IX86\n" +"static inline unsigned long LowDword(unsigned __int64 value) {\n" +" return (unsigned long)value;\n" +"}\n" +"\n" +"static inline unsigned long HighDword(unsigned __int64 value) {\n" +" unsigned long high;\n" +" memcpy(&high, (unsigned char*)&value + sizeof(high), sizeof(high));\n" +" return high;\n" +"}\n" +"#endif\n" +"\n" +"static inline int I32_CLZ(unsigned long mask) {\n" +" if (mask == 0)\n" +" return 32;\n" +" unsigned long index;\n" +" _BitScanReverse(&index, mask);\n" +" return sizeof(unsigned long) * 8 - (index + 1);\n" +"}\n" +"\n" +"static inline int I64_CLZ(unsigned __int64 mask) {\n" +"#if _M_X64\n" +" if (mask == 0) return 64;\n" +" unsigned long index;\n" +" _BitScanReverse64(&index, mask);\n" +" return sizeof(unsigned __int64) * 8 - (index + 1);\n" +"#elif _M_IX86\n" +" int result = I32_CLZ(HighDword(mask));\n" +" if (result == 32)\n" +" result += I32_CLZ(LowDword(mask));\n" +" return result;\n" +"#else\n" +"#error unexpected architecture\n" +"#endif\n" +"}\n" +"\n" +"static inline int I32_CTZ(unsigned long mask) {\n" +" if (mask == 0) return 32;\n" +" unsigned long index;\n" +" _BitScanForward(&index, mask);\n" +" return index;\n" +"}\n" +"\n" +"static inline int I64_CTZ(unsigned __int64 mask) {\n" +"#if _M_X64\n" +" if (mask == 0) return 64;\n" +" unsigned long index;\n" +" _BitScanForward64(&index, mask);\n" +" return index;\n" +"#elif _M_IX86\n" +" int result = I32_CTZ(LowDword(mask));\n" +" if (result == 32)\n" +" result += I32_CTZ(HighDword(mask));\n" +" return result;\n" +"#else\n" +"#error unexpected architecture\n" +"#endif\n" +"}\n" +"\n" +"static inline int I32_POPCNT(unsigned long value) {\n" +" return __popcnt(value);\n" +"}\n" +"\n" +"static inline int I64_POPCNT(unsigned __int64 value) {\n" +"#if _M_X64\n" +" return __popcnt64(value);\n" +"#elif _M_IX86\n" +" return I32_POPCNT(HighDword(value)) + I32_POPCNT(LowDword(value));\n" +"#else\n" +"#error unexpected architecture\n" +"#endif\n" +"}\n" +"\n" +"#else\n" "#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)\n" "#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)\n" "#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)\n" "#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)\n" "#define I32_POPCNT(x) (__builtin_popcount(x))\n" "#define I64_POPCNT(x) (__builtin_popcountll(x))\n" +"#endif\n" "\n" "#define DIV_S(ut, min, x, y) \\\n" " ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n" @@ -136,11 +225,11 @@ const char SECTION_NAME(declarations)[] = "#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, UINT32_MAX, >, x)\n" "#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, UINT64_MAX, >=, x)\n" "\n" -"#define DEFINE_REINTERPRET(name, t1, t2) \\\n" -" static inline t2 name(t1 x) { \\\n" -" t2 result; \\\n" -" memcpy(&result, &x, sizeof(result)); \\\n" -" return result; \\\n" +"#define DEFINE_REINTERPRET(name, t1, t2) \\\n" +" static inline t2 WASM_RT_CC name(t1 x) { \\\n" +" t2 result; \\\n" +" memcpy(&result, &x, sizeof(result)); \\\n" +" return result; \\\n" " }\n" "\n" "DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)\n" @@ -148,4 +237,38 @@ const char SECTION_NAME(declarations)[] = "DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)\n" "DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)\n" "\n" +"#if _MSC_VER\n" +"static inline f32 __vectorcall F32_COPYSIGN(f32 x, f32 y) {\n" +"#if _M_X64 || _M_IX86\n" +" return _mm_cvtss_f32(_mm_or_ps(\n" +" _mm_and_ps(_mm_set_ss(x),\n" +" _mm_castsi128_ps(_mm_set_epi32(0, 0, 0, 0x7fffffffu))),\n" +" _mm_and_ps(_mm_set_ss(y),\n" +" _mm_castsi128_ps(_mm_set_epi32(0, 0, 0, 0x80000000u)))));\n" +"#else\n" +"#error unexpected architecture\n" +"#endif\n" +"}\n" +"\n" +"static inline f64 __vectorcall F64_COPYSIGN(f64 x, f64 y) {\n" +"#if _M_X64 || _M_IX86\n" +" return _mm_cvtsd_f64(_mm_or_pd(\n" +" _mm_and_pd(_mm_set_sd(x), _mm_castsi128_pd(_mm_set_epi32(\n" +" 0, 0, 0x7fffffffu, 0xffffffffu))),\n" +" _mm_and_pd(_mm_set_sd(y),\n" +" _mm_castsi128_pd(_mm_set_epi32(0, 0, 0x80000000u, 0)))));\n" +"#else\n" +"#error unexpected architecture\n" +"#endif\n" +"}\n" +"\n" +"static inline f32 __vectorcall F32_ABS(f32 x) { return F32_COPYSIGN(x, 0.f); }\n" +"static inline f64 __vectorcall F64_ABS(f64 x) { return F64_COPYSIGN(x, 0.f); }\n" +"#else\n" +"#define F32_ABS fabsf\n" +"#define F64_ABS fabs\n" +"#define F32_COPYSIGN copysignf\n" +"#define F64_COPYSIGN copysign\n" +"#endif\n" +"\n" ; diff --git a/src/prebuilt/wasm2c.include.h b/src/prebuilt/wasm2c.include.h index 23319cdef..c3073c862 100644 --- a/src/prebuilt/wasm2c.include.h +++ b/src/prebuilt/wasm2c.include.h @@ -12,6 +12,12 @@ const char SECTION_NAME(top)[] = "#define WASM_RT_MODULE_PREFIX\n" "#endif\n" "\n" +"#if _MSC_VER\n" +"#define WASM_RT_CC __vectorcall\n" +"#else\n" +"#define WASM_RT_CC\n" +"#endif\n" +"\n" "#define WASM_RT_PASTE_(x, y) x ## y\n" "#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)\n" "#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)\n" diff --git a/src/wasm2c.c.tmpl b/src/wasm2c.c.tmpl index 2d188c77c..b0553b80e 100644 --- a/src/wasm2c.c.tmpl +++ b/src/wasm2c.c.tmpl @@ -1,17 +1,29 @@ %%includes #include #include +#if _MSC_VER +#include +#include +#endif %%declarations +#if _MSC_VER +#define UNLIKELY(x) (x) +#define LIKELY(x) (x) +#else #define UNLIKELY(x) __builtin_expect(!!(x), 0) #define LIKELY(x) __builtin_expect(!!(x), 1) +#endif #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) \ +#define FUNC_PROLOGUE(frame_size) \ + if ((++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) || \ + ((wasm_rt_call_stack_size += frame_size) > WASM_RT_MAX_CALL_STACK_SIZE)) \ TRAP(EXHAUSTION) -#define FUNC_EPILOGUE --wasm_rt_call_stack_depth +#define FUNC_EPILOGUE(frame_size) \ + --wasm_rt_call_stack_depth; \ + wasm_rt_call_stack_size -= frame_size #define UNREACHABLE TRAP(UNREACHABLE) @@ -24,19 +36,20 @@ #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_LOAD(name, t1, t2, t3) \ + static inline t3 WASM_RT_CC 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 DEFINE_STORE(name, t1, t2) \ + static inline void WASM_RT_CC 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); @@ -63,12 +76,88 @@ DEFINE_STORE(i64_store8, u8, u64); DEFINE_STORE(i64_store16, u16, u64); DEFINE_STORE(i64_store32, u32, u64); +#if _MSC_VER +#if _M_IX86 +static inline unsigned long LowDword(unsigned __int64 value) { + return (unsigned long)value; +} + +static inline unsigned long HighDword(unsigned __int64 value) { + unsigned long high; + memcpy(&high, (unsigned char*)&value + sizeof(high), sizeof(high)); + return high; +} +#endif + +static inline int I32_CLZ(unsigned long mask) { + if (mask == 0) + return 32; + unsigned long index; + _BitScanReverse(&index, mask); + return sizeof(unsigned long) * 8 - (index + 1); +} + +static inline int I64_CLZ(unsigned __int64 mask) { +#if _M_X64 + if (mask == 0) return 64; + unsigned long index; + _BitScanReverse64(&index, mask); + return sizeof(unsigned __int64) * 8 - (index + 1); +#elif _M_IX86 + int result = I32_CLZ(HighDword(mask)); + if (result == 32) + result += I32_CLZ(LowDword(mask)); + return result; +#else +#error unexpected architecture +#endif +} + +static inline int I32_CTZ(unsigned long mask) { + if (mask == 0) return 32; + unsigned long index; + _BitScanForward(&index, mask); + return index; +} + +static inline int I64_CTZ(unsigned __int64 mask) { +#if _M_X64 + if (mask == 0) return 64; + unsigned long index; + _BitScanForward64(&index, mask); + return index; +#elif _M_IX86 + int result = I32_CTZ(LowDword(mask)); + if (result == 32) + result += I32_CTZ(HighDword(mask)); + return result; +#else +#error unexpected architecture +#endif +} + +static inline int I32_POPCNT(unsigned long value) { + return __popcnt(value); +} + +static inline int I64_POPCNT(unsigned __int64 value) { +#if _M_X64 + return __popcnt64(value); +#elif _M_IX86 + return I32_POPCNT(HighDword(value)) + I32_POPCNT(LowDword(value)); +#else +#error unexpected architecture +#endif +} + +#else #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)) +#endif #define DIV_S(ut, min, x, y) \ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ @@ -133,11 +222,11 @@ DEFINE_STORE(i64_store32, u32, u64); #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 DEFINE_REINTERPRET(name, t1, t2) \ + static inline t2 WASM_RT_CC name(t1 x) { \ + t2 result; \ + memcpy(&result, &x, sizeof(result)); \ + return result; \ } DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) @@ -145,3 +234,37 @@ DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +#if _MSC_VER +static inline f32 __vectorcall F32_COPYSIGN(f32 x, f32 y) { +#if _M_X64 || _M_IX86 + return _mm_cvtss_f32(_mm_or_ps( + _mm_and_ps(_mm_set_ss(x), + _mm_castsi128_ps(_mm_set_epi32(0, 0, 0, 0x7fffffffu))), + _mm_and_ps(_mm_set_ss(y), + _mm_castsi128_ps(_mm_set_epi32(0, 0, 0, 0x80000000u))))); +#else +#error unexpected architecture +#endif +} + +static inline f64 __vectorcall F64_COPYSIGN(f64 x, f64 y) { +#if _M_X64 || _M_IX86 + return _mm_cvtsd_f64(_mm_or_pd( + _mm_and_pd(_mm_set_sd(x), _mm_castsi128_pd(_mm_set_epi32( + 0, 0, 0x7fffffffu, 0xffffffffu))), + _mm_and_pd(_mm_set_sd(y), + _mm_castsi128_pd(_mm_set_epi32(0, 0, 0x80000000u, 0))))); +#else +#error unexpected architecture +#endif +} + +static inline f32 __vectorcall F32_ABS(f32 x) { return F32_COPYSIGN(x, 0.f); } +static inline f64 __vectorcall F64_ABS(f64 x) { return F64_COPYSIGN(x, 0.f); } +#else +#define F32_ABS fabsf +#define F64_ABS fabs +#define F32_COPYSIGN copysignf +#define F64_COPYSIGN copysign +#endif + diff --git a/src/wasm2c.h.tmpl b/src/wasm2c.h.tmpl index ac51cde98..79b70f693 100644 --- a/src/wasm2c.h.tmpl +++ b/src/wasm2c.h.tmpl @@ -11,6 +11,12 @@ extern "C" { #define WASM_RT_MODULE_PREFIX #endif +#if _MSC_VER +#define WASM_RT_CC __vectorcall +#else +#define WASM_RT_CC +#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) diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py index baf08a721..095bd3b3b 100755 --- a/test/run-spec-wasm2c.py +++ b/test/run-spec-wasm2c.py @@ -52,13 +52,8 @@ def F32ToC(f32_bits): 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' + return '%1.9ef' % ReinterpretF32(f32_bits) def ReinterpretF64(f64_bits): @@ -77,10 +72,8 @@ def F64ToC(f64_bits): 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) + return '%1.17e' % ReinterpretF64(f64_bits) def MangleType(t): @@ -295,16 +288,55 @@ def _Action(self, command): raise Error('Unexpected action type: %s' % type_) -def Compile(cc, c_filename, out_dir, *args): - out_dir = os.path.abspath(out_dir) - o_filename = utils.ChangeDir(utils.ChangeExt(c_filename, '.o'), out_dir) - cc.RunWithArgs('-c', '-o', o_filename, c_filename, *args, cwd=out_dir) - return o_filename +class Toolchain(object): + def __init__(self, out_dir, options): + self.out_dir = out_dir + self.msvc = options.msvc + self.cc = utils.Executable(options.cc, *options.cflags, + clean_stdout=self._clean_cc_stdout) + self.link = utils.Executable(options.cc) + + if self.msvc: + self.cc.AppendArg('/nologo') + self.link.AppendArg('/nologo') + + def _clean_cc_stdout(self, stdout): + if self.msvc: + # Strip the first line of output, since cl.exe always prints the name of + # the file it is compiling. + nl = stdout.find(b'\n') + stdout = stdout[nl+1:] + return stdout + + def Compile(self, c_filename, includes=None, defines=None): + includes = includes or [] + defines = defines or [] + + out_dir = os.path.abspath(self.out_dir) + o_filename = utils.ChangeDir(utils.ChangeExt(c_filename, ''), out_dir) + + if self.msvc: + o_filename += '.obj' + args = (['/c', '/Fo' + o_filename, c_filename] + + ['/I' + i for i in includes] + + ['/D' + d for d in defines]) + self.cc.RunWithArgs(*args, cwd=out_dir) + else: + o_filename += '.o' + args = (['-c', '-o', o_filename, c_filename] + + ['-I' + i for i in includes] + + ['-D' + d for d in defines]) + self.cc.RunWithArgs(*args, cwd=out_dir) + return o_filename -def Link(cc, o_filenames, main_exe, out_dir, *args): - args = ['-o', main_exe] + o_filenames + list(args) - cc.RunWithArgs(*args, cwd=out_dir) + def Link(self, o_filenames, main_exe): + if self.msvc: + args = ['/Fe%s.exe' % main_exe] + o_filenames + else: + # Always include libm. + args = ['-o', main_exe] + o_filenames + ['-lm'] + self.link.RunWithArgs(*args, cwd=self.out_dir) def main(args): @@ -319,7 +351,9 @@ def main(args): parser.add_argument('--wasmrt-dir', metavar='PATH', help='directory with wasm-rt files', default=WASM2C_DIR) parser.add_argument('--cc', metavar='PATH', - help='the path to the C compiler', default='cc') + help='the path to the C compiler') + parser.add_argument('--msvc', action='store_true', + help='If set, use MSVC as compiler') parser.add_argument('--cflags', metavar='FLAGS', help='additional flags for C compiler.', action='append', default=[]) @@ -342,6 +376,9 @@ def main(args): parser.add_argument('file', help='wast file.') options = parser.parse_args(args) + if options.cc is None: + options.cc = 'cl' if options.msvc else 'cc' + 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( @@ -357,8 +394,6 @@ def main(args): 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) @@ -375,25 +410,27 @@ def main(args): with open(main_filename, 'w') as out_main_file: out_main_file.write(output.getvalue()) + toolchain = Toolchain(out_dir, options) + o_filenames = [] - includes = '-I%s' % options.wasmrt_dir + includes = ['%s' % options.wasmrt_dir] - # Compile wasm-rt-impl. - wasm_rt_impl_c = os.path.join(options.wasmrt_dir, 'wasm-rt-impl.c') - o_filenames.append(Compile(cc, wasm_rt_impl_c, out_dir, includes)) + if options.compile: + wasm_rt_impl_c = os.path.join(options.wasmrt_dir, 'wasm-rt-impl.c') + o_filenames.append(toolchain.Compile(wasm_rt_impl_c, includes)) 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: - defines = '-DWASM_RT_MODULE_PREFIX=%s' % cwriter.GetModulePrefix(i) - o_filenames.append(Compile(cc, c_filename, out_dir, includes, defines)) + defines = ['WASM_RT_MODULE_PREFIX=%s' % cwriter.GetModulePrefix(i)] + o_filenames.append(toolchain.Compile(c_filename, includes, defines)) if options.compile: main_c = os.path.basename(main_filename) - o_filenames.append(Compile(cc, main_c, out_dir, includes, defines)) + o_filenames.append(toolchain.Compile(main_c, includes)) main_exe = os.path.basename(utils.ChangeExt(json_file_path, '')) - Link(cc, o_filenames, main_exe, out_dir, '-lm') + toolchain.Link(o_filenames, main_exe) if options.compile and options.run: utils.Executable(os.path.join(out_dir, main_exe), @@ -406,9 +443,12 @@ def main(args): 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')) + if sys.version_info[0] == 3: + sys.stderr.write(u'{0}\n'.format(e)) + else: + # 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 f6e10d861..8dcebb01c 100755 --- a/test/run-tests.py +++ b/test/run-tests.py @@ -141,10 +141,6 @@ ] } -# TODO(binji): Add Windows support for compiling using run-spec-wasm2c.py -if IS_WINDOWS: - TOOLS['run-spec-wasm2c'].append(('SKIP', '')) - ROUNDTRIP_TOOLS = ('wat2wasm',) @@ -629,8 +625,8 @@ def _PrintShortStatus(self, info): assert(self.isatty) total_duration = time.time() - self.start_time name = info.GetName() if info else '' - if (self.total - self.skipped): - percent = 100 * (self.passed + self.failed) / (self.total - self.skipped) + if self.total: + percent = 100 * (self.passed + self.failed) / self.total else: percent = 100 status = '[+%d|-%d|%%%d] (%.2fs) %s' % (self.passed, self.failed, @@ -869,6 +865,8 @@ def main(args): parser.add_argument('-p', '--print-cmd', help='print the commands that are run.', action='store_true') + parser.add_argument('--msvc', action='store_true', + help='run wasm2c tests using MSVC compiler') parser.add_argument('patterns', metavar='pattern', nargs='*', help='test patterns.') options = parser.parse_args(args) @@ -885,6 +883,15 @@ def main(args): else: pattern_re = '.*' + if options.msvc: + # Forward the --msvc flag to run-spec-wasm2c.py. This needs to be separated + # into two steps because otherwise Python will complain about modifying a + # tuple. + wasm2c_args_tuple = TOOLS['run-spec-wasm2c'][1] + assert wasm2c_args_tuple[0] == 'ARGS' + wasm2c_args_list = wasm2c_args_tuple[1] + wasm2c_args_list += ['--msvc'] + test_names = FindTestFiles('.txt', pattern_re) if options.list: diff --git a/test/spec-wasm2c-prefix.c b/test/spec-wasm2c-prefix.c index 897872b4e..989389d80 100644 --- a/test/spec-wasm2c-prefix.c +++ b/test/spec-wasm2c-prefix.c @@ -127,28 +127,28 @@ static bool is_equal_u64(u64 x, u64 y) { return x == y; } -static bool is_equal_f32(f32 x, f32 y) { +static bool WASM_RT_CC 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) { +static bool WASM_RT_CC 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) { +static f32 WASM_RT_CC make_nan_f32(u32 x) { x |= 0x7f800000; f32 res; memcpy(&res, &x, sizeof(res)); return res; } -static f64 make_nan_f64(u64 x) { +static f64 WASM_RT_CC make_nan_f64(u64 x) { x |= 0x7ff0000000000000; f64 res; memcpy(&res, &x, sizeof(res)); @@ -175,27 +175,27 @@ static bool is_arithmetic_nan_f64(u64 x) { /* * spectest implementations */ -static void spectest_print(void) { +static void WASM_RT_CC spectest_print(void) { printf("spectest.print()\n"); } -static void spectest_print_i32(uint32_t i) { +static void WASM_RT_CC spectest_print_i32(uint32_t i) { printf("spectest.print_i32(%d)\n", i); } -static void spectest_print_f32(float f) { +static void WASM_RT_CC spectest_print_f32(float f) { printf("spectest.print_f32(%g)\n", f); } -static void spectest_print_i32_f32(uint32_t i, float f) { +static void WASM_RT_CC spectest_print_i32_f32(uint32_t i, float f) { printf("spectest.print_i32_f32(%d %g)\n", i, f); } -static void spectest_print_f64(double d) { +static void WASM_RT_CC spectest_print_f64(double d) { printf("spectest.print_f64(%g)\n", d); } -static void spectest_print_f64_f64(double d1, double d2) { +static void WASM_RT_CC spectest_print_f64_f64(double d1, double d2) { printf("spectest.print_f64_f64(%g %g)\n", d1, d2); } @@ -203,14 +203,14 @@ static wasm_rt_table_t spectest_table; static wasm_rt_memory_t spectest_memory; static uint32_t spectest_global_i32 = 666; -void (*Z_spectestZ_printZ_vv)(void) = &spectest_print; -void (*Z_spectestZ_print_i32Z_vi)(uint32_t) = &spectest_print_i32; -void (*Z_spectestZ_print_f32Z_vf)(float) = &spectest_print_f32; -void (*Z_spectestZ_print_i32_f32Z_vif)(uint32_t, - float) = &spectest_print_i32_f32; -void (*Z_spectestZ_print_f64Z_vd)(double) = &spectest_print_f64; -void (*Z_spectestZ_print_f64_f64Z_vdd)(double, - double) = &spectest_print_f64_f64; +void (WASM_RT_CC *Z_spectestZ_printZ_vv)(void) = &spectest_print; +void (WASM_RT_CC *Z_spectestZ_print_i32Z_vi)(uint32_t) = &spectest_print_i32; +void (WASM_RT_CC *Z_spectestZ_print_f32Z_vf)(float) = &spectest_print_f32; +void (WASM_RT_CC *Z_spectestZ_print_i32_f32Z_vif)(uint32_t, float) + = &spectest_print_i32_f32; +void (WASM_RT_CC *Z_spectestZ_print_f64Z_vd)(double) = &spectest_print_f64; +void (WASM_RT_CC *Z_spectestZ_print_f64_f64Z_vdd)(double, double) + = &spectest_print_f64_f64; wasm_rt_table_t* Z_spectestZ_table = &spectest_table; wasm_rt_memory_t* Z_spectestZ_memory = &spectest_memory; uint32_t* Z_spectestZ_global_i32Z_i = &spectest_global_i32; diff --git a/wasm2c/wasm-rt-impl.c b/wasm2c/wasm-rt-impl.c index 0fcbcf9d4..8ba6f8e04 100644 --- a/wasm2c/wasm-rt-impl.c +++ b/wasm2c/wasm-rt-impl.c @@ -34,6 +34,7 @@ typedef struct FuncType { uint32_t wasm_rt_call_stack_depth; uint32_t g_saved_call_stack_depth; +uint32_t wasm_rt_call_stack_size; jmp_buf g_jmp_buf; FuncType* g_func_types; diff --git a/wasm2c/wasm-rt.h b/wasm2c/wasm-rt.h index 494fde191..c798d8bb2 100644 --- a/wasm2c/wasm-rt.h +++ b/wasm2c/wasm-rt.h @@ -23,6 +23,12 @@ extern "C" { #endif +#if _MSC_VER +#define WASM_RT_NORETURN(x) __declspec(noreturn) x +#else +#define WASM_RT_NORETURN(x) x __attribute__((noreturn)) +#endif + /** Maximum stack depth before trapping. This can be configured by defining * this symbol before including wasm-rt when building the generated c files, * for example: @@ -35,6 +41,22 @@ extern "C" { #define WASM_RT_MAX_CALL_STACK_DEPTH 500 #endif +/** Maximum stack size before trapping. + * + * See the documentation for `wasm_rt_call_stack_size` for documentation on how + * this value is calculated. + * + * This value can be configured by defining this symbol before including + * wasm-rt when building the generated c files, for example: + * + * ``` + * cc -c -DWASM_RT_MAX_CALL_STACK_SIZE=1000000 my_module.c -o my_module.o + * ``` + * */ +#ifndef WASM_RT_MAX_CALL_STACK_SIZE +#define WASM_RT_MAX_CALL_STACK_SIZE (512 * 1024) +#endif + /** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ typedef enum { WASM_RT_TRAP_NONE, /** No error. */ @@ -95,7 +117,7 @@ typedef struct { * The result of `wasm_rt_try` will be the provided trap reason. * * This is typically called by the generated code, and not the embedder. */ -extern void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn)); +extern void WASM_RT_NORETURN(wasm_rt_trap(wasm_rt_trap_t)); /** Register a function type with the given signature. The returned function * index is guaranteed to be the same for all calls with the same signature. @@ -151,16 +173,41 @@ extern uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages); * * ``` * wasm_rt_table_t my_table; - * // 5 elemnets and a maximum of 10 elements. + * // 5 elements and a maximum of 10 elements. * wasm_rt_allocate_table(&my_table, 5, 10); * ``` */ extern void wasm_rt_allocate_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements); -/** Current call stack depth. */ +/** Current call stack depth. + * + * TODO remove? It's only use is debugging now that `wasm_rt_call_stack_size` is + * being used instead. */ extern uint32_t wasm_rt_call_stack_depth; +/** Current call stack size. This is measured by adding the size of all + * parameters and locals in all active stack frames. + * + * For example, the following function: + * + * ``` + * (func (param i32 i64) (local f64) ...) + * ``` + * + * will have a stack frame size of 4 + 8 + 8 = 20 bytes. + * + * TODO include variables introduced from value stack as well? + * + * A C compiler may reduce (or increase) the actual stack size used, but + * `wasm_rt_call_stack_size` will be used instead of the actual stack size for + * calculating whether the call stack is exhausted. + * + * The C compiler may insert additional stack checks, but those will not cause + * a trap via `wasm_rt_trap`. + */ +extern uint32_t wasm_rt_call_stack_size; + #ifdef __cplusplus } #endif