v8/test/common/wasm/wasm-module-runner.cc
Ben L. Titzer 6aa2a25313 [wasm] Add WasmFeatures to enable/detect features
This CL introduces a set of configuration options implemented as
a struct of booleans that together comprise the set of enabled
or detected features. The configuration options replace command-line
flags that were checked deep in the implementation. As such, it is
necessary to plumb them through multiple levels of abstraction.

R=ahaas@chromium.org
CC=mstarzinger@chromium.org
BUG=chromium:868844

Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I1b82f5826e4fd263f68e8cafcd923bac5818a637
Reviewed-on: https://chromium-review.googlesource.com/1163670
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55018}
2018-08-09 10:58:22 +00:00

262 lines
9.8 KiB
C++

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/common/wasm/wasm-module-runner.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "src/property-descriptor.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-interpreter.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace testing {
uint32_t GetInitialMemSize(const WasmModule* module) {
return kWasmPageSize * module->initial_pages;
}
MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
auto enabled_features = WasmFeaturesFromIsolate(isolate);
MaybeHandle<WasmModuleObject> module = isolate->wasm_engine()->SyncCompile(
isolate, enabled_features, thrower, bytes);
DCHECK_EQ(thrower->error(), module.is_null());
if (module.is_null()) return {};
return isolate->wasm_engine()->SyncInstantiate(
isolate, thrower, module.ToHandleChecked(), {}, {});
}
std::shared_ptr<WasmModule> DecodeWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin, bool verify_functions) {
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
auto enabled_features = WasmFeaturesFromIsolate(isolate);
ModuleResult decoding_result = DecodeWasmModule(
enabled_features, module_start, module_end, verify_functions, origin,
isolate->counters(), isolate->allocator());
if (decoding_result.failed()) {
// Module verification failed. throw.
thrower->CompileError("DecodeWasmModule failed: %s",
decoding_result.error_msg().c_str());
}
return std::move(decoding_result.val);
}
bool InterpretWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance,
const char* name, size_t argc,
WasmValue* args) {
MaybeHandle<WasmExportedFunction> maybe_function =
GetExportedFunction(isolate, instance, "main");
Handle<WasmExportedFunction> function;
if (!maybe_function.ToHandle(&function)) {
return false;
}
int function_index = function->function_index();
FunctionSig* signature = instance->module()->functions[function_index].sig;
size_t param_count = signature->parameter_count();
std::unique_ptr<WasmValue[]> arguments(new WasmValue[param_count]);
memcpy(arguments.get(), args, std::min(param_count, argc));
// Fill the parameters up with default values.
for (size_t i = argc; i < param_count; ++i) {
switch (signature->GetParam(i)) {
case kWasmI32:
arguments[i] = WasmValue(int32_t{0});
break;
case kWasmI64:
arguments[i] = WasmValue(int64_t{0});
break;
case kWasmF32:
arguments[i] = WasmValue(0.0f);
break;
case kWasmF64:
arguments[i] = WasmValue(0.0);
break;
default:
UNREACHABLE();
}
}
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;
Zone zone(isolate->allocator(), ZONE_NAME);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
thread->Reset();
// Start an activation so that we can deal with stack overflows. We do not
// finish the activation. An activation is just part of the state of the
// interpreter, and we do not reuse the interpreter anyways. In addition,
// finishing the activation is not correct in all cases, e.g. when the
// execution of the interpreter did not finish after kMaxNumSteps.
thread->StartActivation();
thread->InitFrame(&instance->module()->functions[function_index],
arguments.get());
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
isolate->clear_pending_exception();
return interpreter_result != WasmInterpreter::PAUSED;
}
int32_t RunWasmModuleForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance, int argc,
Handle<Object> argv[]) {
ErrorThrower thrower(isolate, "RunWasmModule");
return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
argv);
}
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end) {
HandleScope scope(isolate);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(module_start, module_end));
if (instance.is_null()) {
return -1;
}
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
nullptr);
}
int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end) {
HandleScope scope(isolate);
ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
MaybeHandle<WasmModuleObject> module =
isolate->wasm_engine()->SyncCompileTranslatedAsmJs(
isolate, &thrower, ModuleWireBytes(module_start, module_end),
Handle<Script>::null(), Vector<const byte>());
DCHECK_EQ(thrower.error(), module.is_null());
if (module.is_null()) return -1;
MaybeHandle<WasmInstanceObject> instance =
isolate->wasm_engine()->SyncInstantiate(
isolate, &thrower, module.ToHandleChecked(),
Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
DCHECK_EQ(thrower.error(), instance.is_null());
if (instance.is_null()) return -1;
return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
nullptr);
}
int32_t InterpretWasmModule(Isolate* isolate,
Handle<WasmInstanceObject> instance,
ErrorThrower* thrower, int32_t function_index,
WasmValue* args, bool* possible_nondeterminism) {
// Don't execute more than 16k steps.
constexpr int kMaxNumSteps = 16 * 1024;
Zone zone(isolate->allocator(), ZONE_NAME);
v8::internal::HandleScope scope(isolate);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
thread->Reset();
// Start an activation so that we can deal with stack overflows. We do not
// finish the activation. An activation is just part of the state of the
// interpreter, and we do not reuse the interpreter anyways. In addition,
// finishing the activation is not correct in all cases, e.g. when the
// execution of the interpreter did not finish after kMaxNumSteps.
thread->StartActivation();
thread->InitFrame(&(instance->module()->functions[function_index]), args);
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception();
*possible_nondeterminism = thread->PossibleNondeterminism();
if (stack_overflow) return 0xDEADBEEF;
if (thread->state() == WasmInterpreter::TRAPPED) return 0xDEADBEEF;
if (interpreter_result == WasmInterpreter::FINISHED)
return thread->GetReturnValue().to<int32_t>();
thrower->RangeError(
"Interpreter did not finish execution within its step bound");
return -1;
}
MaybeHandle<WasmExportedFunction> GetExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
Handle<JSObject> exports_object;
Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
exports_object = Handle<JSObject>::cast(
JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
PropertyDescriptor desc;
Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
isolate, exports_object, main_name, &desc);
if (!property_found.FromMaybe(false)) return {};
if (!desc.value()->IsJSFunction()) return {};
return Handle<WasmExportedFunction>::cast(desc.value());
}
int32_t CallWasmFunctionForTesting(Isolate* isolate,
Handle<WasmInstanceObject> instance,
ErrorThrower* thrower, const char* name,
int argc, Handle<Object> argv[]) {
MaybeHandle<WasmExportedFunction> maybe_export =
GetExportedFunction(isolate, instance, name);
Handle<WasmExportedFunction> main_export;
if (!maybe_export.ToHandle(&main_export)) {
return -1;
}
// Call the JS function.
Handle<Object> undefined = isolate->factory()->undefined_value();
MaybeHandle<Object> retval =
Execution::Call(isolate, main_export, undefined, argc, argv);
// The result should be a number.
if (retval.is_null()) {
DCHECK(isolate->has_pending_exception());
isolate->clear_pending_exception();
thrower->RuntimeError("Calling exported wasm function failed.");
return -1;
}
Handle<Object> result = retval.ToHandleChecked();
if (result->IsSmi()) {
return Smi::ToInt(*result);
}
if (result->IsHeapNumber()) {
return static_cast<int32_t>(HeapNumber::cast(*result)->value());
}
thrower->RuntimeError(
"Calling exported wasm function failed: Return value should be number");
return -1;
}
void SetupIsolateForWasmModule(Isolate* isolate) {
WasmJs::Install(isolate, true);
}
} // namespace testing
} // namespace wasm
} // namespace internal
} // namespace v8