diff options
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/spectest-interp.cc | 567 | ||||
-rw-r--r-- | src/tools/wasm-interp.cc | 183 |
2 files changed, 404 insertions, 346 deletions
diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc index 047a3dc1..b0837ca7 100644 --- a/src/tools/spectest-interp.cc +++ b/src/tools/spectest-interp.cc @@ -19,6 +19,7 @@ #include <cinttypes> #include <cstdio> #include <cstdlib> +#include <map> #include <memory> #include <string> #include <vector> @@ -29,6 +30,7 @@ #include "src/error-formatter.h" #include "src/feature.h" #include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" #include "src/interp/interp.h" #include "src/literal.h" #include "src/option-parser.h" @@ -139,7 +141,8 @@ class Action { ActionType type = ActionType::Invoke; std::string module_name; std::string field_name; - TypedValues args; + ValueTypes types; + Values args; }; template <CommandType TypeEnum> @@ -222,9 +225,9 @@ class JSONParser { wabt::Result ParseTypeVector(TypeVector* out_types); wabt::Result ParseConst(TypedValue* out_value); wabt::Result ParseConstValue(TypedValue* out_value, - string_view type_str, - string_view value_str); - wabt::Result ParseConstVector(TypedValues* out_values); + string_view type_str, + string_view value_str); + wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values); wabt::Result ParseExpectedValue(ExpectedValue* out_value); wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values); wabt::Result ParseAction(Action* out_action); @@ -500,8 +503,8 @@ wabt::Result JSONParser::ParseConst(TypedValue* out_value) { } wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, - string_view type_str, - string_view value_str) { + string_view type_str, + string_view value_str) { const char* value_start = value_str.data(); const char* value_end = value_str.data() + value_str.size(); if (type_str == "i32") { @@ -511,8 +514,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid i32 literal"); return wabt::Result::Error; } - out_value->type = Type::I32; - out_value->value.i32 = value; + out_value->type = ValueType::I32; + out_value->value.Set(value); } else if (type_str == "f32") { uint32_t value_bits; if (Failed(ParseInt32(value_start, value_end, &value_bits, @@ -520,8 +523,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid f32 literal"); return wabt::Result::Error; } - out_value->type = Type::F32; - out_value->value.f32_bits = value_bits; + out_value->type = ValueType::F32; + out_value->value.Set(Bitcast<f32>(value_bits)); } else if (type_str == "i64") { uint64_t value; if (Failed(ParseInt64(value_start, value_end, &value, @@ -529,8 +532,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid i64 literal"); return wabt::Result::Error; } - out_value->type = Type::I64; - out_value->value.i64 = value; + out_value->type = ValueType::I64; + out_value->value.Set(value); } else if (type_str == "f64") { uint64_t value_bits; if (Failed((ParseInt64(value_start, value_end, &value_bits, @@ -538,19 +541,19 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid f64 literal"); return wabt::Result::Error; } - out_value->type = Type::F64; - out_value->value.f64_bits = value_bits; + out_value->type = ValueType::F64; + out_value->value.Set(Bitcast<f64>(value_bits)); } else if (type_str == "v128") { v128 value_bits; if (Failed(ParseUint128(value_start, value_end, &value_bits))) { PrintError("invalid v128 literal"); return wabt::Result::Error; } - out_value->type = Type::V128; - out_value->value.vec128 = value_bits; + out_value->type = ValueType::V128; + out_value->value.Set(value_bits); } else if (type_str == "nullref") { - out_value->type = Type::Nullref; - out_value->value.ref = {RefType::Null, 0}; + out_value->type = ValueType::Nullref; + out_value->value.Set(Ref::Null); } else if (type_str == "hostref") { uint32_t value; if (Failed(ParseInt32(value_start, value_end, &value, @@ -558,16 +561,19 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid hostref literal"); return wabt::Result::Error; } - out_value->type = Type::Hostref; - out_value->value.ref = {RefType::Host, value}; + out_value->type = ValueType::Hostref; + // TODO: hack, just whatever ref is at this index; but skip null (which is + // always 0). + out_value->value.Set(Ref{value + 1}); } else if (type_str == "funcref") { uint32_t value; - if (Failed(ParseInt32(value_start, value_end, &value, ParseIntType::UnsignedOnly))) { + if (Failed(ParseInt32(value_start, value_end, &value, + ParseIntType::UnsignedOnly))) { PrintError("invalid funcref literal"); return wabt::Result::Error; } - out_value->type = Type::Funcref; - out_value->value.ref = {RefType::Func, value}; + out_value->type = ValueType::Funcref; + out_value->value.Set(Ref{value}); } else { PrintError("unknown concrete type: \"%s\"", type_str.to_string().c_str()); return wabt::Result::Error; @@ -586,13 +592,12 @@ wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value) { EXPECT("}"); if (type_str == "f32" || type_str == "f64") { + out_value->value.type = type_str == "f32" ? ValueType::F32 : ValueType::F64; if (value_str == "nan:canonical") { - out_value->value.type = type_str == "f32" ? Type::F32 : Type::F64; out_value->is_expected_nan = true; out_value->expectedNan = ExpectedNan::Canonical; return wabt::Result::Ok; } else if (value_str == "nan:arithmetic") { - out_value->value.type = type_str == "f32" ? Type::F32 : Type::F64; out_value->is_expected_nan = true; out_value->expectedNan = ExpectedNan::Arithmetic; return wabt::Result::Ok; @@ -603,7 +608,8 @@ wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value) { return ParseConstValue(&out_value->value, type_str, value_str); } -wabt::Result JSONParser::ParseExpectedValues(std::vector<ExpectedValue>* out_values) { +wabt::Result JSONParser::ParseExpectedValues( + std::vector<ExpectedValue>* out_values) { out_values->clear(); EXPECT("["); bool first = true; @@ -619,7 +625,7 @@ wabt::Result JSONParser::ParseExpectedValues(std::vector<ExpectedValue>* out_val return wabt::Result::Ok; } -wabt::Result JSONParser::ParseConstVector(TypedValues* out_values) { +wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) { out_values->clear(); EXPECT("["); bool first = true; @@ -627,9 +633,10 @@ wabt::Result JSONParser::ParseConstVector(TypedValues* out_values) { if (!first) { EXPECT(","); } - TypedValue value; - CHECK_RESULT(ParseConst(&value)); - out_values->push_back(value); + TypedValue tv; + CHECK_RESULT(ParseConst(&tv)); + out_types->push_back(tv.type); + out_values->push_back(tv.value); first = false; } return wabt::Result::Ok; @@ -655,7 +662,7 @@ wabt::Result JSONParser::ParseAction(Action* out_action) { if (out_action->type == ActionType::Invoke) { EXPECT(","); EXPECT_KEY("args"); - CHECK_RESULT(ParseConstVector(&out_action->args)); + CHECK_RESULT(ParseConstVector(&out_action->types, &out_action->args)); } EXPECT("}"); return wabt::Result::Ok; @@ -859,21 +866,34 @@ wabt::Result JSONParser::ParseScript(Script* out_script) { return wabt::Result::Ok; } +struct ActionResult { + ValueTypes types; + Values values; + Trap::Ptr trap; +}; + class CommandRunner { public: CommandRunner(); - wabt::Result Run(const Script& script); int passed() const { return passed_; } int total() const { return total_; } private: + using ExportMap = std::map<std::string, Extern::Ptr>; + using Registry = std::map<std::string, ExportMap>; + void WABT_PRINTF_FORMAT(3, 4) PrintError(uint32_t line_number, const char* format, ...); - ExecResult RunAction(int line_number, - const Action* action, - RunVerbosity verbose); + ActionResult RunAction(int line_number, + const Action* action, + RunVerbosity verbose); + + interp::Module::Ptr ReadModule(string_view module_filename, Errors* errors); + Extern::Ptr GetImport(const std::string&, const std::string&); + void PopulateImports(const interp::Module::Ptr&, RefVec*); + void PopulateExports(const Instance::Ptr&, ExportMap*); wabt::Result OnModuleCommand(const ModuleCommand*); wabt::Result OnActionCommand(const ActionCommand*); @@ -890,61 +910,67 @@ class CommandRunner { void TallyCommand(wabt::Result); wabt::Result ReadInvalidTextModule(string_view module_filename, - Environment* env, const std::string& header); wabt::Result ReadInvalidModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc); + string_view module_filename, + ModuleType module_type, + const char* desc); wabt::Result ReadUnlinkableModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc); - - Environment env_; - Executor executor_; - DefinedModule* last_module_ = nullptr; + string_view module_filename, + ModuleType module_type, + const char* desc); + + Store store_; + Registry registry_; // Used when importing. + Registry instances_; // Used when referencing module by name in invoke. + ExportMap last_instance_; int passed_ = 0; int total_ = 0; std::string source_filename_; }; -static interp::Result PrintCallback(const HostFunc* func, - const interp::FuncSignature* sig, - const TypedValues& args, - TypedValues& results) { - printf("called host "); - WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, args, - results, interp::ResultType::Ok); - return interp::ResultType::Ok; -} +CommandRunner::CommandRunner() : store_(s_features) { + auto&& spectest = registry_["spectest"]; + + // Initialize print functions for the spec test. + struct { + const char* name; + interp::FuncType type; + } const print_funcs[] = { + {"print", interp::FuncType{{}, {}}}, + {"print_i32", interp::FuncType{{ValueType::I32}, {}}}, + {"print_f32", interp::FuncType{{ValueType::F32}, {}}}, + {"print_f64", interp::FuncType{{ValueType::F64}, {}}}, + {"print_i32_f32", interp::FuncType{{ValueType::I32, ValueType::F32}, {}}}, + {"print_f64_f64", interp::FuncType{{ValueType::F64, ValueType::F64}, {}}}, + }; + + for (auto&& print : print_funcs) { + auto import_name = StringPrintf("spectest.%s", print.name); + spectest[print.name] = HostFunc::New( + store_, print.type, + [=](const Values& params, Values& results, Trap::Ptr* trap) -> wabt::Result { + printf("called host "); + WriteCall(s_stdout_stream.get(), import_name, print.type, params, + results, *trap); + return wabt::Result::Ok; + }); + } -static void InitEnvironment(Environment* env) { - HostModule* host_module = env->AppendHostModule("spectest"); - host_module->AppendFuncExport("print", {{}, {}}, PrintCallback); - host_module->AppendFuncExport("print_i32", {{Type::I32}, {}}, PrintCallback); - host_module->AppendFuncExport("print_f32", {{Type::F32}, {}}, PrintCallback); - host_module->AppendFuncExport("print_f64", {{Type::F64}, {}}, PrintCallback); - host_module->AppendFuncExport("print_i32_f32", {{Type::I32, Type::F32}, {}}, - PrintCallback); - host_module->AppendFuncExport("print_f64_f64", {{Type::F64, Type::F64}, {}}, - PrintCallback); - - host_module->AppendTableExport("table", Type::Funcref, Limits(10, 20)); - host_module->AppendMemoryExport("memory", Limits(1, 2)); - - host_module->AppendGlobalExport("global_i32", false, uint32_t(666)); - host_module->AppendGlobalExport("global_i64", false, uint64_t(666)); - host_module->AppendGlobalExport("global_f32", false, float(666.6f)); - host_module->AppendGlobalExport("global_f64", false, double(666.6)); -} + spectest["table"] = + interp::Table::New(store_, TableType{ValueType::Funcref, Limits{10, 20}}); -CommandRunner::CommandRunner() - : env_(s_features), executor_(&env_, s_trace_stream, s_thread_options) { - InitEnvironment(&env_); + spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}}); + + spectest["global_i32"] = interp::Global::New( + store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666})); + spectest["global_i64"] = interp::Global::New( + store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666})); + spectest["global_f32"] = interp::Global::New( + store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666})); + spectest["global_f64"] = interp::Global::New( + store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666})); } wabt::Result CommandRunner::Run(const Script& script) { @@ -1009,58 +1035,50 @@ void CommandRunner::PrintError(uint32_t line_number, const char* format, ...) { printf("%s:%u: %s\n", source_filename_.c_str(), line_number, buffer); } -static ExecResult GetGlobalExportByName(Environment* env, - interp::Module* module, - string_view name) { - interp::Export* export_ = module->GetExport(name); - if (!export_) { - return ExecResult(interp::ResultType::UnknownExport); - } - if (export_->kind != ExternalKind::Global) { - return ExecResult(interp::ResultType::ExportKindMismatch); +ActionResult CommandRunner::RunAction(int line_number, + const Action* action, + RunVerbosity verbose) { + ExportMap& module = !action->module_name.empty() + ? instances_[action->module_name] + : last_instance_; + Extern::Ptr extern_ = module[action->field_name]; + if (!extern_) { + PrintError(line_number, "unknown invoke \"%s.%s\"", + action->module_name.c_str(), action->field_name.c_str()); + return {}; } - interp::Global* global = env->GetGlobal(export_->index); - return ExecResult(interp::ResultType::Ok, {global->typed_value}); -} - -ExecResult CommandRunner::RunAction(int line_number, - const Action* action, - RunVerbosity verbose) { - interp::Module* module; - if (!action->module_name.empty()) { - module = env_.FindModule(action->module_name); - } else { - module = env_.GetLastModule(); - } - assert(module); - - ExecResult exec_result; + ActionResult result; switch (action->type) { - case ActionType::Invoke: - exec_result = - executor_.RunExportByName(module, action->field_name, action->args); + case ActionType::Invoke: { + auto* func = cast<interp::Func>(extern_.get()); + func->Call(store_, action->args, result.values, &result.trap, + s_trace_stream); + result.types = func->type().results; if (verbose == RunVerbosity::Verbose) { - WriteCall(s_stdout_stream.get(), string_view(), action->field_name, - action->args, exec_result.values, exec_result.result); + WriteCall(s_stdout_stream.get(), action->field_name, func->type(), + action->args, result.values, result.trap); } break; + } - case ActionType::Get: - exec_result = GetGlobalExportByName(&env_, module, action->field_name); + case ActionType::Get: { + auto* global = cast<interp::Global>(extern_.get()); + result.values.push_back(global->Get()); + result.types.push_back(global->type().type); break; + } default: WABT_UNREACHABLE; } - return exec_result; + return result; } wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename, - Environment* env, - const std::string& header) { + const std::string& header) { std::vector<uint8_t> file_data; wabt::Result result = ReadFile(module_filename, &file_data); std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer( @@ -1078,101 +1096,128 @@ wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename, return result; } -static wabt::Result ReadModule(string_view module_filename, - Environment* env, - Errors* errors, - DefinedModule** out_module) { - wabt::Result result; +interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename, + Errors* errors) { std::vector<uint8_t> file_data; - *out_module = nullptr; + if (Failed(ReadFile(module_filename, &file_data))) { + return {}; + } - result = ReadFile(module_filename, &file_data); - if (Succeeded(result)) { - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - const bool kFailOnCustomSectionError = true; - ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, - kStopOnFirstError, kFailOnCustomSectionError); - result = ReadBinaryInterp(env, file_data.data(), file_data.size(), options, - errors, out_module); - - if (Succeeded(result)) { - if (s_verbose) { - env->DisassembleModule(s_stdout_stream.get(), *out_module); - } - } + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + ModuleDesc module_desc; + if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc))) { + return {}; } - return result; + + if (s_verbose) { + module_desc.istream.Disassemble(s_stdout_stream.get()); + } + + return interp::Module::New(store_, module_desc); } wabt::Result CommandRunner::ReadInvalidModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc) { + string_view module_filename, + ModuleType module_type, + const char* desc) { std::string header = StringPrintf( "%s:%d: %s passed", source_filename_.c_str(), line_number, desc); switch (module_type) { case ModuleType::Text: { - return ReadInvalidTextModule(module_filename, env, header); + return ReadInvalidTextModule(module_filename, header); } case ModuleType::Binary: { - DefinedModule* module; Errors errors; - wabt::Result result = ReadModule(module_filename, env, &errors, &module); - if (Failed(result)) { + auto module = ReadModule(module_filename, &errors); + if (!module) { FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header, PrintHeader::Once); - return result; + return wabt::Result::Error; + } else { + return wabt::Result::Ok; } - return result; } } WABT_UNREACHABLE; } +Extern::Ptr CommandRunner::GetImport(const std::string& module, + const std::string& name) { + auto mod_iter = registry_.find(module); + if (mod_iter != registry_.end()) { + auto extern_iter = mod_iter->second.find(name); + if (extern_iter != mod_iter->second.end()) { + return extern_iter->second; + } + } + return {}; +} + +void CommandRunner::PopulateImports(const interp::Module::Ptr& module, + RefVec* imports) { + for (auto&& import : module->desc().imports) { + auto extern_ = GetImport(import.type.module, import.type.name); + imports->push_back(extern_ ? extern_.ref() : Ref::Null); + } +} + +void CommandRunner::PopulateExports(const Instance::Ptr& instance, + ExportMap* map) { + map->clear(); + interp::Module::Ptr module{store_, instance->module()}; + for (size_t i = 0; i < module->export_types().size(); ++i) { + const ExportType& export_type = module->export_types()[i]; + (*map)[export_type.name] = store_.UnsafeGet<Extern>(instance->exports()[i]); + } +} + wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) { - Environment::MarkPoint mark = env_.Mark(); Errors errors; - wabt::Result result = ReadModule(command->filename, &env_, - &errors, &last_module_); + auto module = ReadModule(command->filename, &errors); FormatErrorsToFile(errors, Location::Type::Binary); - if (Failed(result)) { - env_.ResetToMarkPoint(mark); + if (!module) { PrintError(command->line, "error reading module: \"%s\"", command->filename.c_str()); return wabt::Result::Error; } - ExecResult exec_result = executor_.Initialize(last_module_); - if (!exec_result.ok()) { - env_.ResetToMarkPoint(mark); - WriteResult(s_stdout_stream.get(), "error initializing module", - exec_result.result); + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (trap) { + assert(!instance); + PrintError(command->line, "error instantiating module: \"%s\"", + trap->message().c_str()); return wabt::Result::Error; } + PopulateExports(instance, &last_instance_); if (!command->name.empty()) { - last_module_->name = command->name; - env_.EmplaceModuleBinding(command->name, - Binding(env_.GetModuleCount() - 1)); + instances_[command->name] = last_instance_; } return wabt::Result::Ok; } wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Verbose); - if (!exec_result.ok()) { + if (result.trap) { PrintError(command->line, "unexpected trap: %s", - ResultToString(exec_result.result).c_str()); + result.trap->message().c_str()); return wabt::Result::Error; } @@ -1181,12 +1226,8 @@ wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { wabt::Result CommandRunner::OnAssertMalformedCommand( const AssertMalformedCommand* command) { - Environment env(s_features); - InitEnvironment(&env); - - wabt::Result result = - ReadInvalidModule(command->line, command->filename, &env, command->type, - "assert_malformed"); + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_malformed"); if (Succeeded(result)) { PrintError(command->line, "expected module to be malformed: \"%s\"", command->filename.c_str()); @@ -1197,55 +1238,52 @@ wabt::Result CommandRunner::OnAssertMalformedCommand( } wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) { - Index module_index; if (!command->name.empty()) { - module_index = env_.FindModuleIndex(command->name); + auto instance_iter = instances_.find(command->name); + if (instance_iter == instances_.end()) { + PrintError(command->line, "unknown module in register"); + return wabt::Result::Error; + } + registry_[command->as] = instance_iter->second; } else { - module_index = env_.GetLastModuleIndex(); - } - - if (module_index == kInvalidIndex) { - PrintError(command->line, "unknown module in register"); - return wabt::Result::Error; + registry_[command->as] = last_instance_; } - env_.EmplaceRegisteredModuleBinding(command->as, Binding(module_index)); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertUnlinkableCommand( const AssertUnlinkableCommand* command) { Errors errors; - wabt::Result result = - ReadModule(command->filename, &env_, &errors, &last_module_); - - if (Failed(result)) { - std::string header = StringPrintf("%s:%d: assert_unlinkable passed", - source_filename_.c_str(), command->line); - FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header, - PrintHeader::Once); - return wabt::Result::Ok; + auto module = ReadModule(command->filename, &errors); + + if (!module) { + PrintError(command->line, "unable to compile unlinkable module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; } - ExecResult exec_result = executor_.Initialize(last_module_); - if (exec_result.ok()) { + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { PrintError(command->line, "expected module to be unlinkable: \"%s\"", command->filename.c_str()); return wabt::Result::Error; } - WriteResult(s_stdout_stream.get(), "assert_unlinkable passed", - exec_result.result); + // TODO: Change to one-line error. + PrintError(command->line, "assert_unlinkable passed:\n error: %s", + trap->message().c_str()); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertInvalidCommand( const AssertInvalidCommand* command) { - Environment env(s_features); - InitEnvironment(&env); - - wabt::Result result = ReadInvalidModule( - command->line, command->filename, &env, command->type, "assert_invalid"); + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_invalid"); if (Succeeded(result)) { PrintError(command->line, "expected module to be invalid: \"%s\"", command->filename.c_str()); @@ -1258,94 +1296,127 @@ wabt::Result CommandRunner::OnAssertInvalidCommand( wabt::Result CommandRunner::OnAssertUninstantiableCommand( const AssertUninstantiableCommand* command) { Errors errors; - DefinedModule* module; - wabt::Result result = ReadModule(command->filename, &env_, &errors, &module); - FormatErrorsToFile(errors, Location::Type::Binary); + auto module = ReadModule(command->filename, &errors); - if (Succeeded(result)) { - ExecResult exec_result = executor_.Initialize(module); - if (exec_result.ok()) { - PrintError(command->line, "expected instantiation error: \"%s\"", - command->filename.c_str()); - result = wabt::Result::Error; - } else { - result = wabt::Result::Ok; - } - } else { - PrintError(command->line, "error reading module: \"%s\"", + if (!module) { + PrintError(command->line, "unable to compile uninstantiable module: \"%s\"", command->filename.c_str()); - result = wabt::Result::Error; + return wabt::Result::Error; } - // Don't reset env_ here; if the start function fails, the environment is - // still modified. For example, a table may have been populated with a - // function from this module. - return result; -} + RefVec imports; + PopulateImports(module, &imports); -static bool TypedValuesAreEqual(const TypedValue& tv1, const TypedValue& tv2) { - if (tv1.type != tv2.type) { - return false; + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { + PrintError(command->line, "expected module to be uninstantiable: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; } - switch (tv1.type) { + // TODO: print error when assertion passes. +#if 0 + PrintError(command->line, "assert_uninstantiable passed: %s", + trap->message().c_str()); +#endif + return wabt::Result::Ok; +} + +static bool TypedValuesAreEqual(const TypedValue& expected, + const TypedValue& actual) { + assert(expected.type == actual.type || IsReference(expected.type)); + switch (expected.type) { case Type::I32: - return tv1.value.i32 == tv2.value.i32; + return expected.value.Get<u32>() == actual.value.Get<u32>(); + case Type::F32: - return tv1.value.f32_bits == tv2.value.f32_bits; + return Bitcast<u32>(expected.value.Get<f32>()) == + Bitcast<u32>(actual.value.Get<f32>()); + case Type::I64: - return tv1.value.i64 == tv2.value.i64; + return expected.value.Get<u64>() == actual.value.Get<u64>(); + case Type::F64: - return tv1.value.f64_bits == tv2.value.f64_bits; + return Bitcast<u64>(expected.value.Get<f64>()) == + Bitcast<u64>(actual.value.Get<f64>()); + case Type::V128: - return tv1.value.vec128 == tv2.value.vec128; + return expected.value.Get<v128>() == actual.value.Get<v128>(); + case Type::Nullref: - return true; + return actual.value.Get<Ref>() == Ref::Null; + case Type::Funcref: - return tv1.value.ref.index == tv2.value.ref.index; case Type::Hostref: - return tv1.value.ref.index == tv2.value.ref.index; + return expected.value.Get<Ref>() == actual.value.Get<Ref>(); + default: WABT_UNREACHABLE; } } +static bool WABT_VECTORCALL IsCanonicalNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + const u32 kQuietNegNan = 0xffc00000U; + u32 bits = Bitcast<u32>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsCanonicalNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + const u64 kQuietNegNan = 0xfff8000000000000ULL; + u64 bits = Bitcast<u64>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + return (Bitcast<u32>(val) & kQuietNan) == kQuietNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + return (Bitcast<u64>(val) & kQuietNan) == kQuietNan; +} + wabt::Result CommandRunner::OnAssertReturnCommand( const AssertReturnCommand* command) { - ExecResult exec_result = + ActionResult action_result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (!exec_result.ok()) { + if (action_result.trap) { PrintError(command->line, "unexpected trap: %s", - ResultToString(exec_result.result).c_str()); + action_result.trap->message().c_str()); return wabt::Result::Error; } - if (exec_result.values.size() != command->expected.size()) { + if (action_result.values.size() != command->expected.size()) { PrintError(command->line, "result length mismatch in assert_return: expected %" PRIzd ", got %" PRIzd, - command->expected.size(), exec_result.values.size()); + command->expected.size(), action_result.values.size()); return wabt::Result::Error; } wabt::Result result = wabt::Result::Ok; - for (size_t i = 0; i < exec_result.values.size(); ++i) { + for (size_t i = 0; i < action_result.values.size(); ++i) { const ExpectedValue& expected = command->expected[i]; - const TypedValue& actual = exec_result.values[i]; + TypedValue actual{action_result.types[i], action_result.values[i]}; + if (expected.is_expected_nan) { bool is_nan; if (expected.expectedNan == ExpectedNan::Arithmetic) { if (expected.value.type == Type::F64) { - is_nan = IsArithmeticNan(actual.value.f64_bits); + is_nan = IsArithmeticNan(actual.value.Get<f64>()); } else { - is_nan = IsArithmeticNan(actual.value.f32_bits); + is_nan = IsArithmeticNan(actual.value.Get<f32>()); } } else if (expected.expectedNan == ExpectedNan::Canonical) { if (expected.value.type == Type::F64) { - is_nan = IsCanonicalNan(actual.value.f64_bits); + is_nan = IsCanonicalNan(actual.value.Get<f64>()); } else { - is_nan = IsCanonicalNan(actual.value.f32_bits); + is_nan = IsCanonicalNan(actual.value.Get<f32>()); } } else { WABT_UNREACHABLE; @@ -1356,7 +1427,7 @@ wabt::Result CommandRunner::OnAssertReturnCommand( result = wabt::Result::Error; } } else if (expected.value.type == Type::Funcref) { - if (actual.type != Type::Funcref) { + if (!store_.HasValueType(actual.value.Get<Ref>(), Type::Funcref)) { PrintError(command->line, "mismatch in result %" PRIzd " of assert_return: expected funcref, got %s", @@ -1379,28 +1450,32 @@ wabt::Result CommandRunner::OnAssertReturnCommand( wabt::Result CommandRunner::OnAssertTrapCommand( const AssertTrapCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (exec_result.ok()) { + if (!result.trap) { PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); return wabt::Result::Error; } PrintError(command->line, "assert_trap passed: %s", - ResultToString(exec_result.result).c_str()); + result.trap->message().c_str()); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertExhaustionCommand( const AssertExhaustionCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (exec_result.result.type != interp::ResultType::TrapCallStackExhausted && - exec_result.result.type != interp::ResultType::TrapValueStackExhausted) { - PrintError(command->line, "expected call stack exhaustion"); + if (!result.trap || result.trap->message() != "call stack exhausted") { + PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); return wabt::Result::Error; } + // TODO: print message when assertion passes. +#if 0 + PrintError(command->line, "assert_exhaustion passed: %s", + result.trap->message().c_str()); +#endif return wabt::Result::Ok; } diff --git a/src/tools/wasm-interp.cc b/src/tools/wasm-interp.cc index 6126331b..733c54cd 100644 --- a/src/tools/wasm-interp.cc +++ b/src/tools/wasm-interp.cc @@ -16,7 +16,6 @@ #include <algorithm> #include <cassert> -#include <cinttypes> #include <cstdio> #include <cstdlib> #include <memory> @@ -24,18 +23,13 @@ #include <vector> #include "src/binary-reader.h" -#include "src/cast.h" #include "src/error-formatter.h" #include "src/feature.h" #include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" #include "src/interp/interp.h" -#include "src/literal.h" #include "src/option-parser.h" -#include "src/resolve-names.h" #include "src/stream.h" -#include "src/validator.h" -#include "src/wast-lexer.h" -#include "src/wast-parser.h" using namespace wabt; using namespace wabt::interp; @@ -52,10 +46,7 @@ static Features s_features; static std::unique_ptr<FileStream> s_log_stream; static std::unique_ptr<FileStream> s_stdout_stream; -enum class RunVerbosity { - Quiet = 0, - Verbose = 1, -}; +static Store s_store; static const char s_description[] = R"( read a file in the wasm binary format, and run in it a stack-based @@ -117,112 +108,104 @@ static void ParseOptions(int argc, char** argv) { parser.Parse(argc, argv); } -static void RunAllExports(interp::Module* module, - Executor* executor, - RunVerbosity verbose) { - TypedValues args; - TypedValues results; - for (const interp::Export& export_ : module->exports) { - if (export_.kind != ExternalKind::Func) { - continue; - } - ExecResult exec_result = executor->RunExport(&export_, args); - if (verbose == RunVerbosity::Verbose) { - WriteCall(s_stdout_stream.get(), string_view(), export_.name, args, - exec_result.values, exec_result.result); - } - } -} +Result RunAllExports(const Instance::Ptr& instance, Errors* errors) { + Result result = Result::Ok; -static wabt::Result ReadModule(const char* module_filename, - Environment* env, - Errors* errors, - DefinedModule** out_module) { - wabt::Result result; - std::vector<uint8_t> file_data; + auto module = s_store.UnsafeGet<Module>(instance->module()); + auto&& module_desc = module->desc(); - *out_module = nullptr; - - result = ReadFile(module_filename, &file_data); - if (Succeeded(result)) { - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - const bool kFailOnCustomSectionError = true; - ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, - kStopOnFirstError, kFailOnCustomSectionError); - result = ReadBinaryInterp(env, file_data.data(), file_data.size(), options, - errors, out_module); - - if (Succeeded(result)) { - if (s_verbose) { - env->DisassembleModule(s_stdout_stream.get(), *out_module); + for (auto&& export_ : module_desc.exports) { + if (export_.type.type->kind != ExternalKind::Func) { + continue; + } + auto* func_type = cast<FuncType>(export_.type.type.get()); + if (func_type->params.empty()) { + if (s_trace_stream) { + s_trace_stream->Writef(">>> running export \"%s\":\n", + export_.type.name.c_str()); } + auto func = s_store.UnsafeGet<Func>(instance->funcs()[export_.index]); + Values params; + Values results; + Trap::Ptr trap; + result |= func->Call(s_store, params, results, &trap, s_trace_stream); + WriteCall(s_stdout_stream.get(), export_.type.name, *func_type, params, + results, trap); } } + return result; } -static interp::Result PrintCallback(const HostFunc* func, - const interp::FuncSignature* sig, - const TypedValues& args, - TypedValues& results) { - printf("called host "); - WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, args, - results, interp::ResultType::Ok); - return interp::ResultType::Ok; -} +Result ReadAndInstantiateModule(const char* module_filename, + Errors* errors, + Instance::Ptr* out_instance) { + auto* stream = s_stdout_stream.get(); + std::vector<uint8_t> file_data; + CHECK_RESULT(ReadFile(module_filename, &file_data)); + + ModuleDesc module_desc; + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc)); + + if (s_verbose) { + module_desc.istream.Disassemble(stream); + } -static void InitEnvironment(Environment* env) { - if (s_host_print) { - auto* host_module = env->AppendHostModule("host"); - host_module->on_unknown_func_export = - [](Environment* env, HostModule* host_module, string_view name, - Index sig_index) -> Index { - if (name != "print") { - return kInvalidIndex; - } + auto module = Module::New(s_store, module_desc); + + RefVec imports; + for (auto&& import : module_desc.imports) { + if (import.type.type->kind == ExternKind::Func && + ((s_host_print && import.type.module == "host" && + import.type.name == "print") || + s_dummy_import_func)) { + auto func_type = *cast<FuncType>(import.type.type.get()); + auto import_name = StringPrintf("%s.%s", import.type.module.c_str(), + import.type.name.c_str()); + + auto host_func = + HostFunc::New(s_store, func_type, + [=](const Values& params, Values& results, + Trap::Ptr* trap) -> Result { + printf("called host "); + WriteCall(stream, import_name, func_type, params, + results, *trap); + return Result::Ok; + }); + imports.push_back(host_func.ref()); + continue; + } - return host_module->AppendFuncExport(name, sig_index, PrintCallback) - .second; - }; + // By default, just push an null reference. This won't resolve, and + // instantiation will fail. + imports.push_back(Ref::Null); } - if (s_dummy_import_func) { - env->on_unknown_module = [](Environment* env, string_view name) { - auto* host_module = env->AppendHostModule(name); - host_module->on_unknown_func_export = - [](Environment* env, HostModule* host_module, string_view name, - Index sig_index) -> Index { - return host_module->AppendFuncExport(name, sig_index, PrintCallback) - .second; - }; - return true; - }; + RefPtr<Trap> trap; + *out_instance = Instance::Instantiate(s_store, module.ref(), imports, &trap); + if (!*out_instance) { + // TODO: change to "initializing" + WriteTrap(stream, "error initialiazing module", trap); + return Result::Error; } -} -static wabt::Result ReadAndRunModule(const char* module_filename) { - wabt::Result result; - Environment env(s_features); - InitEnvironment(&env); + return Result::Ok; +} +static Result ReadAndRunModule(const char* module_filename) { Errors errors; - DefinedModule* module = nullptr; - result = ReadModule(module_filename, &env, &errors, &module); - FormatErrorsToFile(errors, Location::Type::Binary); - if (Succeeded(result)) { - Executor executor(&env, s_trace_stream, s_thread_options); - ExecResult exec_result = executor.Initialize(module); - if (exec_result.ok()) { - if (s_run_all_exports) { - RunAllExports(module, &executor, RunVerbosity::Verbose); - } - } else { - WriteResult(s_stdout_stream.get(), "error initialiazing module", - exec_result.result); - return wabt::Result::Error; - } + Instance::Ptr instance; + Result result = ReadAndInstantiateModule(module_filename, &errors, &instance); + if (Succeeded(result) && s_run_all_exports) { + RunAllExports(instance, &errors); } + FormatErrorsToFile(errors, Location::Type::Binary); return result; } |