[wasm][debug] Remove redirection to interpreter
This removes the {RedirectToWasmInterpreter} runtime function and the respective method from {WasmDebugInfo}. Some tests test specifically the interaction between compiled code and the interpreter. They are irrelevant now and are deleted. R=thibaudm@chromium.org Bug: v8:10389 Change-Id: I38330fcb523f7c65968fdf03abc60af3392bdcc8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2164793 Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Thibaud Michaud <thibaudm@chromium.org> Cr-Commit-Position: refs/heads/master@{#67427}
This commit is contained in:
parent
bce121c7c5
commit
1fb1db1792
@ -1340,18 +1340,6 @@ RUNTIME_FUNCTION(Runtime_WasmNumCodeSpaces) {
|
||||
return *isolate->factory()->NewNumberFromSize(num_spaces);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_RedirectToWasmInterpreter) {
|
||||
DCHECK_EQ(2, args.length());
|
||||
HandleScope scope(isolate);
|
||||
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(function_index, 1);
|
||||
Handle<WasmDebugInfo> debug_info =
|
||||
WasmInstanceObject::GetOrCreateDebugInfo(instance);
|
||||
WasmDebugInfo::RedirectToInterpreter(debug_info,
|
||||
Vector<int>(&function_index, 1));
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmTraceMemory) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
|
@ -519,7 +519,6 @@ namespace internal {
|
||||
F(NewRegExpWithBacktrackLimit, 3, 1) \
|
||||
F(PrepareFunctionForOptimization, -1, 1) \
|
||||
F(PrintWithNameForAssert, 2, 1) \
|
||||
F(RedirectToWasmInterpreter, 2, 1) \
|
||||
F(RunningInSimulator, 0, 1) \
|
||||
F(RuntimeEvaluateREPL, 1, 1) \
|
||||
F(SerializeDeserializeNow, 0, 1) \
|
||||
|
@ -963,42 +963,6 @@ wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting(
|
||||
return interp_handle->raw()->interpreter();
|
||||
}
|
||||
|
||||
// static
|
||||
void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
|
||||
Vector<int> func_indexes) {
|
||||
Isolate* isolate = debug_info->GetIsolate();
|
||||
// Ensure that the interpreter is instantiated.
|
||||
GetOrCreateInterpreterHandle(isolate, debug_info);
|
||||
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
|
||||
wasm::NativeModule* native_module = instance->module_object().native_module();
|
||||
const wasm::WasmModule* module = instance->module();
|
||||
|
||||
// We may modify the wasm jump table.
|
||||
wasm::NativeModuleModificationScope native_module_modification_scope(
|
||||
native_module);
|
||||
|
||||
for (int func_index : func_indexes) {
|
||||
DCHECK_LE(0, func_index);
|
||||
DCHECK_GT(module->functions.size(), func_index);
|
||||
// Note that this is just a best effort check. Multiple threads can still
|
||||
// race at redirecting the same function to the interpreter, which is OK.
|
||||
if (native_module->IsRedirectedToInterpreter(func_index)) continue;
|
||||
|
||||
wasm::WasmCodeRefScope code_ref_scope;
|
||||
wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry(
|
||||
isolate->wasm_engine(), native_module->enabled_features(), func_index,
|
||||
module->functions[func_index].sig);
|
||||
std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
|
||||
func_index, result.code_desc, result.frame_slot_count,
|
||||
result.tagged_parameter_slots,
|
||||
result.protected_instructions_data.as_vector(),
|
||||
result.source_positions.as_vector(), wasm::WasmCode::kInterpreterEntry,
|
||||
wasm::ExecutionTier::kInterpreter, wasm::kNoDebugging);
|
||||
native_module->PublishCode(std::move(wasm_code));
|
||||
DCHECK(native_module->IsRedirectedToInterpreter(func_index));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool WasmDebugInfo::RunInterpreter(Isolate* isolate,
|
||||
Handle<WasmDebugInfo> debug_info,
|
||||
|
@ -843,11 +843,6 @@ class WasmDebugInfo : public Struct {
|
||||
V8_EXPORT_PRIVATE static wasm::WasmInterpreter* SetupForTesting(
|
||||
Handle<WasmInstanceObject>);
|
||||
|
||||
// Make a set of functions always execute in the interpreter without setting
|
||||
// breakpoints.
|
||||
V8_EXPORT_PRIVATE static void RedirectToInterpreter(Handle<WasmDebugInfo>,
|
||||
Vector<int> func_indexes);
|
||||
|
||||
// Execute the specified function in the interpreter. Read arguments from the
|
||||
// {argument_values} vector and write to {return_values} on regular exit.
|
||||
// The frame_pointer will be used to identify the new activation of the
|
||||
|
@ -298,7 +298,6 @@ v8_source_set("cctest_sources") {
|
||||
"wasm/test-wasm-debug-evaluate.cc",
|
||||
"wasm/test-wasm-debug-evaluate.h",
|
||||
"wasm/test-wasm-import-wrapper-cache.cc",
|
||||
"wasm/test-wasm-interpreter-entry.cc",
|
||||
"wasm/test-wasm-serialization.cc",
|
||||
"wasm/test-wasm-shared-engine.cc",
|
||||
"wasm/test-wasm-stack.cc",
|
||||
|
@ -490,7 +490,6 @@
|
||||
'test-wasm-codegen/*': [SKIP],
|
||||
'test-wasm-debug-evaluate/*': [SKIP],
|
||||
'test-wasm-import-wrapper-cache/*': [SKIP],
|
||||
'test-wasm-interpreter-entry/*': [SKIP],
|
||||
'test-wasm-serialization/*': [SKIP],
|
||||
'test-wasm-shared-engine/*': [SKIP],
|
||||
'test-wasm-stack/*': [SKIP],
|
||||
|
@ -1,260 +0,0 @@
|
||||
// Copyright 2017 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 <cstdint>
|
||||
|
||||
#include "src/base/overflowing-math.h"
|
||||
#include "src/codegen/assembler-inl.h"
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
#include "test/cctest/compiler/value-helper.h"
|
||||
#include "test/cctest/wasm/wasm-run-utils.h"
|
||||
#include "test/common/wasm/wasm-macro-gen.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
/**
|
||||
* We test the interface from Wasm compiled code to the Wasm interpreter by
|
||||
* building a module with two functions. The external function is called from
|
||||
* this test, and will be compiled code. It takes its arguments and passes them
|
||||
* on to the internal function, which will be redirected to the interpreter.
|
||||
* If the internal function has an i64 parameter, is has to be replaced by two
|
||||
* i32 parameters on the external function.
|
||||
* The internal function just converts all its arguments to f64, sums them up
|
||||
* and returns the sum.
|
||||
*/
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
class ArgPassingHelper {
|
||||
public:
|
||||
ArgPassingHelper(WasmRunnerBase* runner, WasmFunctionCompiler* inner_compiler,
|
||||
std::initializer_list<uint8_t> bytes_inner_function,
|
||||
std::initializer_list<uint8_t> bytes_outer_function,
|
||||
const T& expected_lambda)
|
||||
: isolate_(runner->main_isolate()),
|
||||
expected_lambda_(expected_lambda),
|
||||
debug_info_(WasmInstanceObject::GetOrCreateDebugInfo(
|
||||
runner->builder().instance_object())) {
|
||||
std::vector<uint8_t> inner_code{bytes_inner_function};
|
||||
inner_compiler->Build(inner_code.data(),
|
||||
inner_code.data() + inner_code.size());
|
||||
|
||||
std::vector<uint8_t> outer_code{bytes_outer_function};
|
||||
runner->Build(outer_code.data(), outer_code.data() + outer_code.size());
|
||||
|
||||
int funcs_to_redict[] = {
|
||||
static_cast<int>(inner_compiler->function_index())};
|
||||
runner->builder().SetExecutable();
|
||||
WasmDebugInfo::RedirectToInterpreter(debug_info_,
|
||||
ArrayVector(funcs_to_redict));
|
||||
main_fun_wrapper_ = runner->builder().WrapCode(runner->function_index());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void CheckCall(Args... args) {
|
||||
Handle<Object> arg_objs[] = {isolate_->factory()->NewNumber(args)...};
|
||||
|
||||
uint64_t num_interpreted_before = debug_info_->NumInterpretedCalls();
|
||||
Handle<Object> global(isolate_->context().global_object(), isolate_);
|
||||
MaybeHandle<Object> retval = Execution::Call(
|
||||
isolate_, main_fun_wrapper_, global, arraysize(arg_objs), arg_objs);
|
||||
uint64_t num_interpreted_after = debug_info_->NumInterpretedCalls();
|
||||
// Check that we really went through the interpreter.
|
||||
CHECK_EQ(num_interpreted_before + 1, num_interpreted_after);
|
||||
// Check the result.
|
||||
double result = retval.ToHandleChecked()->Number();
|
||||
double expected = expected_lambda_(args...);
|
||||
CHECK_DOUBLE_EQ(expected, result);
|
||||
}
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
T expected_lambda_;
|
||||
Handle<WasmDebugInfo> debug_info_;
|
||||
Handle<JSFunction> main_fun_wrapper_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static ArgPassingHelper<T> GetHelper(
|
||||
WasmRunnerBase* runner, WasmFunctionCompiler* inner_compiler,
|
||||
std::initializer_list<uint8_t> bytes_inner_function,
|
||||
std::initializer_list<uint8_t> bytes_outer_function,
|
||||
const T& expected_lambda) {
|
||||
return ArgPassingHelper<T>(runner, inner_compiler, bytes_inner_function,
|
||||
bytes_outer_function, expected_lambda);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Pass int32_t, return int32_t.
|
||||
TEST(TestArgumentPassing_int32) {
|
||||
WasmRunner<int32_t, int32_t> runner(ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 = runner.NewFunction<int32_t, int32_t>();
|
||||
|
||||
auto helper = GetHelper(
|
||||
&runner, &f2,
|
||||
{// Return 2*<0> + 1.
|
||||
WASM_I32_ADD(WASM_I32_MUL(WASM_I32V_1(2), WASM_GET_LOCAL(0)), WASM_ONE)},
|
||||
{// Call f2 with param <0>.
|
||||
WASM_GET_LOCAL(0), WASM_CALL_FUNCTION0(f2.function_index())},
|
||||
[](int32_t a) {
|
||||
return base::AddWithWraparound(base::MulWithWraparound(2, a), 1);
|
||||
});
|
||||
|
||||
FOR_INT32_INPUTS(v) { helper.CheckCall(v); }
|
||||
}
|
||||
|
||||
// Pass int64_t, return double.
|
||||
TEST(TestArgumentPassing_double_int64) {
|
||||
WasmRunner<double, int32_t, int32_t> runner(ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 = runner.NewFunction<double, int64_t>();
|
||||
|
||||
auto helper = GetHelper(
|
||||
&runner, &f2,
|
||||
{// Return (double)<0>.
|
||||
WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(0))},
|
||||
{// Call f2 with param (<0> | (<1> << 32)).
|
||||
WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(0)),
|
||||
WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)),
|
||||
WASM_I64V_1(32))),
|
||||
WASM_CALL_FUNCTION0(f2.function_index())},
|
||||
[](int32_t a, int32_t b) {
|
||||
int64_t a64 = static_cast<int64_t>(a) & 0xFFFFFFFF;
|
||||
int64_t b64 = static_cast<uint64_t>(static_cast<int64_t>(b)) << 32;
|
||||
return static_cast<double>(a64 | b64);
|
||||
});
|
||||
|
||||
FOR_INT32_INPUTS(v1) {
|
||||
FOR_INT32_INPUTS(v2) { helper.CheckCall(v1, v2); }
|
||||
}
|
||||
|
||||
FOR_INT64_INPUTS(v) {
|
||||
int32_t v1 = static_cast<int32_t>(v);
|
||||
int32_t v2 = static_cast<int32_t>(v >> 32);
|
||||
helper.CheckCall(v1, v2);
|
||||
helper.CheckCall(v2, v1);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass double, return int64_t.
|
||||
TEST(TestArgumentPassing_int64_double) {
|
||||
// Outer function still returns double.
|
||||
WasmRunner<double, double> runner(ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 = runner.NewFunction<int64_t, double>();
|
||||
|
||||
auto helper = GetHelper(
|
||||
&runner, &f2,
|
||||
{// Return (int64_t)<0>.
|
||||
WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0))},
|
||||
{// Call f2 with param <0>, convert returned value back to double.
|
||||
WASM_F64_SCONVERT_I64(WASM_SEQ(
|
||||
WASM_GET_LOCAL(0), WASM_CALL_FUNCTION0(f2.function_index())))},
|
||||
[](double d) { return d; });
|
||||
|
||||
for (int64_t i : compiler::ValueHelper::int64_vector()) {
|
||||
helper.CheckCall(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass float, return double.
|
||||
TEST(TestArgumentPassing_float_double) {
|
||||
WasmRunner<double, float> runner(ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 = runner.NewFunction<double, float>();
|
||||
|
||||
auto helper = GetHelper(
|
||||
&runner, &f2,
|
||||
{// Return 2*(double)<0> + 1.
|
||||
WASM_F64_ADD(
|
||||
WASM_F64_MUL(WASM_F64(2), WASM_F64_CONVERT_F32(WASM_GET_LOCAL(0))),
|
||||
WASM_F64(1))},
|
||||
{// Call f2 with param <0>.
|
||||
WASM_GET_LOCAL(0), WASM_CALL_FUNCTION0(f2.function_index())},
|
||||
[](float f) { return 2. * static_cast<double>(f) + 1.; });
|
||||
|
||||
FOR_FLOAT32_INPUTS(f) { helper.CheckCall(f); }
|
||||
}
|
||||
|
||||
// Pass two doubles, return double.
|
||||
TEST(TestArgumentPassing_double_double) {
|
||||
WasmRunner<double, double, double> runner(ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 = runner.NewFunction<double, double, double>();
|
||||
|
||||
auto helper = GetHelper(&runner, &f2,
|
||||
{// Return <0> + <1>.
|
||||
WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))},
|
||||
{// Call f2 with params <0>, <1>.
|
||||
WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
|
||||
WASM_CALL_FUNCTION0(f2.function_index())},
|
||||
[](double a, double b) { return a + b; });
|
||||
|
||||
FOR_FLOAT64_INPUTS(d1) {
|
||||
FOR_FLOAT64_INPUTS(d2) { helper.CheckCall(d1, d2); }
|
||||
}
|
||||
}
|
||||
|
||||
// Pass int32_t, int64_t, float and double, return double.
|
||||
TEST(TestArgumentPassing_AllTypes) {
|
||||
// The second and third argument will be combined to an i64.
|
||||
WasmRunner<double, int32_t, int32_t, int32_t, float, double> runner(
|
||||
ExecutionTier::kTurbofan);
|
||||
WasmFunctionCompiler& f2 =
|
||||
runner.NewFunction<double, int32_t, int64_t, float, double>();
|
||||
|
||||
auto helper = GetHelper(
|
||||
&runner, &f2,
|
||||
{
|
||||
// Convert all arguments to double, add them and return the sum.
|
||||
WASM_F64_ADD( // <0+1+2> + <3>
|
||||
WASM_F64_ADD( // <0+1> + <2>
|
||||
WASM_F64_ADD( // <0> + <1>
|
||||
WASM_F64_SCONVERT_I32(
|
||||
WASM_GET_LOCAL(0)), // <0> to double
|
||||
WASM_F64_SCONVERT_I64(
|
||||
WASM_GET_LOCAL(1))), // <1> to double
|
||||
WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double
|
||||
WASM_GET_LOCAL(3)) // <3>
|
||||
},
|
||||
{WASM_GET_LOCAL(0), // first arg
|
||||
WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), // second arg
|
||||
WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(2)),
|
||||
WASM_I64V_1(32))),
|
||||
WASM_GET_LOCAL(3), // third arg
|
||||
WASM_GET_LOCAL(4), // fourth arg
|
||||
WASM_CALL_FUNCTION0(f2.function_index())},
|
||||
[](int32_t a, int32_t b, int32_t c, float d, double e) {
|
||||
return 0. + a + (static_cast<int64_t>(b) & 0xFFFFFFFF) +
|
||||
((static_cast<int64_t>(c) & 0xFFFFFFFF) << 32) + d + e;
|
||||
});
|
||||
|
||||
auto CheckCall = [&](int32_t a, int64_t b, float c, double d) {
|
||||
int32_t b0 = static_cast<int32_t>(b);
|
||||
int32_t b1 = static_cast<int32_t>(b >> 32);
|
||||
helper.CheckCall(a, b0, b1, c, d);
|
||||
helper.CheckCall(a, b1, b0, c, d);
|
||||
};
|
||||
|
||||
Vector<const int32_t> test_values_i32 = compiler::ValueHelper::int32_vector();
|
||||
Vector<const int64_t> test_values_i64 = compiler::ValueHelper::int64_vector();
|
||||
Vector<const float> test_values_f32 = compiler::ValueHelper::float32_vector();
|
||||
Vector<const double> test_values_f64 =
|
||||
compiler::ValueHelper::float64_vector();
|
||||
size_t max_len =
|
||||
std::max(std::max(test_values_i32.size(), test_values_i64.size()),
|
||||
std::max(test_values_f32.size(), test_values_f64.size()));
|
||||
for (size_t i = 0; i < max_len; ++i) {
|
||||
int32_t i32 = test_values_i32[i % test_values_i32.size()];
|
||||
int64_t i64 = test_values_i64[i % test_values_i64.size()];
|
||||
float f32 = test_values_f32[i % test_values_f32.size()];
|
||||
double f64 = test_values_f64[i % test_values_f64.size()];
|
||||
CheckCall(i32, i64, f32, f64);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -826,10 +826,6 @@
|
||||
'wasm/module-memory': [SKIP],
|
||||
'wasm/shared-memory-gc-stress': [SKIP],
|
||||
|
||||
# Redirection to the interpreter is non-deterministic with multiple isolates.
|
||||
'wasm/interpreter-mixed': [SKIP],
|
||||
'wasm/worker-interpreter': [SKIP],
|
||||
|
||||
# The {FreezeWasmLazyCompilation} runtime function sets a flag in the native
|
||||
# module, which causes a data-race if the native module is shared between
|
||||
# isolates.
|
||||
|
@ -1,53 +0,0 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-gc
|
||||
|
||||
load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
// Build two instances, instance 2 is interpreted, and calls instance 1 (via
|
||||
// C_WASM_ENTRY), instance 1 then calls JS, which triggers GC.
|
||||
|
||||
let builder1 = new WasmModuleBuilder();
|
||||
|
||||
function call_gc() {
|
||||
print('Triggering GC.');
|
||||
gc();
|
||||
print('Survived GC.');
|
||||
}
|
||||
let func1_sig = makeSig(new Array(8).fill(kWasmI32), [kWasmI32]);
|
||||
let imp = builder1.addImport('q', 'gc', kSig_v_v);
|
||||
let func1 = builder1.addFunction('func1', func1_sig)
|
||||
.addBody([
|
||||
kExprLocalGet, 0, // -
|
||||
kExprCallFunction, imp
|
||||
])
|
||||
.exportFunc();
|
||||
let instance1 = builder1.instantiate({q: {gc: call_gc}});
|
||||
|
||||
let builder2 = new WasmModuleBuilder();
|
||||
|
||||
let func1_imp = builder2.addImport('q', 'func1', func1_sig);
|
||||
let func2 = builder2.addFunction('func2', kSig_i_i)
|
||||
.addBody([
|
||||
kExprLocalGet, 0, // 1
|
||||
kExprLocalGet, 0, // 2
|
||||
kExprLocalGet, 0, // 3
|
||||
kExprLocalGet, 0, // 4
|
||||
kExprLocalGet, 0, // 5
|
||||
kExprLocalGet, 0, // 6
|
||||
kExprLocalGet, 0, // 7
|
||||
kExprLocalGet, 0, // 8
|
||||
kExprCallFunction, func1_imp
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
let instance2 = builder2.instantiate({q: {func1: instance1.exports.func1}});
|
||||
|
||||
%RedirectToWasmInterpreter(
|
||||
instance2, parseInt(instance2.exports.func2.name));
|
||||
|
||||
// Call with 1. This will be passed by the C_WASM_ENTRY via the stack, and the
|
||||
// GC will try to dereference it (before the bug fix).
|
||||
instance2.exports.func2(1);
|
@ -1,222 +0,0 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-gc
|
||||
|
||||
load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
// =============================================================================
|
||||
// Tests in this file test the interaction between the wasm interpreter and
|
||||
// compiled code.
|
||||
// =============================================================================
|
||||
|
||||
// The stack trace contains file path, replace it by "file".
|
||||
let stripPath = s => s.replace(/[^ (]*interpreter-mixed\.js/g, 'file');
|
||||
|
||||
function checkStack(stack, expected_lines) {
|
||||
print('stack: ' + stack);
|
||||
let lines = stack.split('\n');
|
||||
assertEquals(expected_lines.length, lines.length);
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
let test =
|
||||
typeof expected_lines[i] == 'string' ? assertEquals : assertMatches;
|
||||
test(expected_lines[i], lines[i], 'line ' + i);
|
||||
}
|
||||
}
|
||||
|
||||
(function testMemoryGrowBetweenInterpretedAndCompiled() {
|
||||
// grow_memory can be called from interpreted or compiled code, and changes
|
||||
// should be reflected in either execution.
|
||||
var builder = new WasmModuleBuilder();
|
||||
var grow_body = [kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero];
|
||||
var load_body = [kExprLocalGet, 0, kExprI32LoadMem, 0, 0];
|
||||
var store_body = [kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0];
|
||||
builder.addFunction('grow_memory', kSig_i_i).addBody(grow_body).exportFunc();
|
||||
builder.addFunction('load', kSig_i_i).addBody(load_body).exportFunc();
|
||||
builder.addFunction('store', kSig_v_ii).addBody(store_body).exportFunc();
|
||||
var grow_interp_function =
|
||||
builder.addFunction('grow_memory_interpreted', kSig_i_i)
|
||||
.addBody(grow_body)
|
||||
.exportFunc();
|
||||
var load_interp_function = builder.addFunction('load_interpreted', kSig_i_i)
|
||||
.addBody(load_body)
|
||||
.exportFunc();
|
||||
var kNumPages = 2;
|
||||
var kMaxPages = 10;
|
||||
builder.addMemory(kNumPages, kMaxPages, false);
|
||||
var instance = builder.instantiate();
|
||||
var exp = instance.exports;
|
||||
%RedirectToWasmInterpreter(instance, grow_interp_function.index);
|
||||
%RedirectToWasmInterpreter(instance, load_interp_function.index);
|
||||
|
||||
// Initially, we can load from offset 12, but not OOB.
|
||||
var oob_index = kNumPages * kPageSize;
|
||||
var initial_interpreted = %WasmNumInterpretedCalls(instance);
|
||||
assertEquals(0, exp.load(12));
|
||||
assertEquals(0, exp.load_interpreted(12));
|
||||
assertTraps(kTrapMemOutOfBounds, () => exp.load(oob_index));
|
||||
assertTraps(
|
||||
kTrapMemOutOfBounds, () => exp.load_interpreted(oob_index));
|
||||
// Grow by 2 pages from compiled code, and ensure that this is reflected in
|
||||
// the interpreter.
|
||||
assertEquals(kNumPages, exp.grow_memory(2));
|
||||
kNumPages += 2;
|
||||
assertEquals(kNumPages, exp.grow_memory_interpreted(0));
|
||||
assertEquals(kNumPages, exp.grow_memory(0));
|
||||
// Now we can load from the previous OOB index.
|
||||
assertEquals(0, exp.load(oob_index));
|
||||
assertEquals(0, exp.load_interpreted(oob_index));
|
||||
// Set new OOB index and ensure that it traps.
|
||||
oob_index = kNumPages * kPageSize;
|
||||
assertTraps(kTrapMemOutOfBounds, () => exp.load(oob_index));
|
||||
assertTraps(
|
||||
kTrapMemOutOfBounds, () => exp.load_interpreted(oob_index));
|
||||
// Grow by another page in the interpreter, and ensure that this is reflected
|
||||
// in compiled code.
|
||||
assertEquals(kNumPages, exp.grow_memory_interpreted(1));
|
||||
kNumPages += 1;
|
||||
assertEquals(kNumPages, exp.grow_memory_interpreted(0));
|
||||
assertEquals(kNumPages, exp.grow_memory(0));
|
||||
// Now we can store to the previous OOB index and read it back in both
|
||||
// environments.
|
||||
exp.store(oob_index, 47);
|
||||
assertEquals(47, exp.load(oob_index));
|
||||
assertEquals(47, exp.load_interpreted(oob_index));
|
||||
// We cannot grow beyong kMaxPages.
|
||||
assertEquals(-1, exp.grow_memory(kMaxPages - kNumPages + 1));
|
||||
assertEquals(-1, exp.grow_memory_interpreted(kMaxPages - kNumPages + 1));
|
||||
// Overall, we executed 9 functions in the interpreter.
|
||||
assertEquals(initial_interpreted + 9, %WasmNumInterpretedCalls(instance));
|
||||
})();
|
||||
|
||||
function createTwoInstancesCallingEachOther(inner_throws = false) {
|
||||
let builder1 = new WasmModuleBuilder();
|
||||
|
||||
let id_imp = builder1.addImport('q', 'id', kSig_i_i);
|
||||
let plus_one = builder1.addFunction('plus_one', kSig_i_i)
|
||||
.addBody([
|
||||
kExprLocalGet, 0, // -
|
||||
kExprI32Const, 1, // -
|
||||
kExprI32Add, // -
|
||||
kExprCallFunction, id_imp
|
||||
])
|
||||
.exportFunc();
|
||||
function imp(i) {
|
||||
if (inner_throws) throw new Error('i=' + i);
|
||||
return i;
|
||||
}
|
||||
let instance1 = builder1.instantiate({q: {id: imp}});
|
||||
|
||||
let builder2 = new WasmModuleBuilder();
|
||||
|
||||
let plus_one_imp = builder2.addImport('q', 'plus_one', kSig_i_i);
|
||||
let plus_two = builder2.addFunction('plus_two', kSig_i_i)
|
||||
.addBody([
|
||||
// Call import, add one more.
|
||||
kExprLocalGet, 0, // -
|
||||
kExprCallFunction, plus_one_imp, // -
|
||||
kExprI32Const, 1, // -
|
||||
kExprI32Add
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
let instance2 =
|
||||
builder2.instantiate({q: {plus_one: instance1.exports.plus_one}});
|
||||
return [instance1, instance2];
|
||||
}
|
||||
|
||||
function redirectToInterpreter(
|
||||
instance1, instance2, redirect_plus_one, redirect_plus_two) {
|
||||
// Redirect functions to the interpreter.
|
||||
if (redirect_plus_one) {
|
||||
%RedirectToWasmInterpreter(instance1,
|
||||
parseInt(instance1.exports.plus_one.name));
|
||||
}
|
||||
if (redirect_plus_two) {
|
||||
%RedirectToWasmInterpreter(instance2,
|
||||
parseInt(instance2.exports.plus_two.name));
|
||||
}
|
||||
}
|
||||
|
||||
(function testImportFromOtherInstance() {
|
||||
print("testImportFromOtherInstance");
|
||||
// Three runs: Break in instance 1, break in instance 2, or both.
|
||||
for (let run = 0; run < 3; ++run) {
|
||||
print(" - run " + run);
|
||||
(() => {
|
||||
// Trigger a GC to ensure that the underlying native module is not a cached
|
||||
// one from a previous run, with functions already redirected to the
|
||||
// interpreter. This is not observable from pure JavaScript, but this is
|
||||
// observable with the internal runtime functions used in this test.
|
||||
// Run in a local scope to ensure previous native modules are
|
||||
// unreachable.
|
||||
gc();
|
||||
let [instance1, instance2] = createTwoInstancesCallingEachOther();
|
||||
let interpreted_before_1 = %WasmNumInterpretedCalls(instance1);
|
||||
let interpreted_before_2 = %WasmNumInterpretedCalls(instance2);
|
||||
// Call plus_two, which calls plus_one.
|
||||
assertEquals(9, instance2.exports.plus_two(7));
|
||||
// Nothing interpreted:
|
||||
assertEquals(interpreted_before_1, %WasmNumInterpretedCalls(instance1));
|
||||
assertEquals(interpreted_before_2, %WasmNumInterpretedCalls(instance2));
|
||||
// Now redirect functions to the interpreter.
|
||||
redirectToInterpreter(instance1, instance2, run != 1, run != 0);
|
||||
// Call plus_two, which calls plus_one.
|
||||
assertEquals(9, instance2.exports.plus_two(7));
|
||||
// TODO(6668): Fix patching of instances which imported others' code.
|
||||
//assertEquals(interpreted_before_1 + (run == 1 ? 0 : 1),
|
||||
// %WasmNumInterpretedCalls(instance1));
|
||||
assertEquals(interpreted_before_2 + (run == 0 ? 0 : 1),
|
||||
%WasmNumInterpretedCalls(instance2))
|
||||
})();
|
||||
}
|
||||
})();
|
||||
|
||||
(function testStackTraceThroughCWasmEntry() {
|
||||
print("testStackTraceThroughCWasmEntry");
|
||||
for (let run = 0; run < 3; ++run) {
|
||||
print(" - run " + run);
|
||||
let [instance1, instance2] = createTwoInstancesCallingEachOther(true);
|
||||
redirectToInterpreter(instance1, instance2, run != 1, run != 0);
|
||||
|
||||
try {
|
||||
// Call plus_two, which calls plus_one.
|
||||
instance2.exports.plus_two(7);
|
||||
assertUnreachable('should trap because of unreachable instruction');
|
||||
} catch (e) {
|
||||
checkStack(stripPath(e.stack), [
|
||||
'Error: i=8', // -
|
||||
/^ at imp \(file:\d+:29\)$/, // -
|
||||
' at plus_one (<anonymous>:wasm-function[1]:0x3b)', // -
|
||||
' at plus_two (<anonymous>:wasm-function[1]:0x3e)', // -
|
||||
/^ at testStackTraceThroughCWasmEntry \(file:\d+:25\)$/, // -
|
||||
/^ at file:\d+:3$/
|
||||
]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
(function testInterpreterPreservedOnTierUp() {
|
||||
print(arguments.callee.name);
|
||||
var builder = new WasmModuleBuilder();
|
||||
var fun_body = [kExprI32Const, 23];
|
||||
var fun = builder.addFunction('fun', kSig_i_v).addBody(fun_body).exportFunc();
|
||||
var instance = builder.instantiate();
|
||||
var exp = instance.exports;
|
||||
|
||||
// Initially the interpreter is not being called.
|
||||
var initial_interpreted = %WasmNumInterpretedCalls(instance);
|
||||
assertEquals(23, exp.fun());
|
||||
assertEquals(initial_interpreted + 0, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// Redirection will cause the interpreter to be called.
|
||||
%RedirectToWasmInterpreter(instance, fun.index);
|
||||
assertEquals(23, exp.fun());
|
||||
assertEquals(initial_interpreted + 1, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// Requesting a tier-up still ensure the interpreter is being called.
|
||||
%WasmTierUpFunction(instance, fun.index);
|
||||
assertEquals(23, exp.fun());
|
||||
assertEquals(initial_interpreted + 2, %WasmNumInterpretedCalls(instance));
|
||||
})();
|
@ -1,68 +0,0 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-gc
|
||||
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
(function TestPostInterpretedModule() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
let add = builder.addFunction("add", kSig_i_ii)
|
||||
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
|
||||
.exportFunc();
|
||||
|
||||
// Trigger a GC to ensure that the underlying native module is not a cached
|
||||
// one from a previous run, with functions already redirected to the
|
||||
// interpreter. This is not observable from pure JavaScript, but this is
|
||||
// observable with the internal runtime functions used in this test.
|
||||
gc();
|
||||
|
||||
let module = builder.toModule();
|
||||
let instance = new WebAssembly.Instance(module);
|
||||
let exp = instance.exports;
|
||||
|
||||
let workerScript = `
|
||||
var instance = null;
|
||||
onmessage = function(message) {
|
||||
try {
|
||||
if (message.command == 'module') {
|
||||
instance = new WebAssembly.Instance(message.module);
|
||||
postMessage('OK');
|
||||
}
|
||||
if (message.command == 'call') {
|
||||
let result = instance.exports.add(40, 2);
|
||||
postMessage(result);
|
||||
}
|
||||
} catch(e) {
|
||||
postMessage('ERROR: ' + e);
|
||||
}
|
||||
}
|
||||
`;
|
||||
let worker = new Worker(workerScript, {type: 'string'});
|
||||
|
||||
// Call method without using the interpreter.
|
||||
var initial_interpreted = %WasmNumInterpretedCalls(instance);
|
||||
assertEquals(23, exp.add(20, 3));
|
||||
assertEquals(initial_interpreted + 0, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// Send module to the worker, still not interpreting.
|
||||
worker.postMessage({ command:'module', module:module });
|
||||
assertEquals('OK', worker.getMessage());
|
||||
worker.postMessage({ command:'call' });
|
||||
assertEquals(42, worker.getMessage());
|
||||
assertEquals(initial_interpreted + 0, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// Switch to the interpreter and call method.
|
||||
%RedirectToWasmInterpreter(instance, add.index);
|
||||
assertEquals(23, exp.add(20, 3));
|
||||
assertEquals(initial_interpreted + 1, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// Let worker call interpreted function.
|
||||
worker.postMessage({ command:'call' });
|
||||
assertEquals(42, worker.getMessage());
|
||||
assertEquals(initial_interpreted + 1, %WasmNumInterpretedCalls(instance));
|
||||
|
||||
// All done.
|
||||
worker.terminate();
|
||||
})();
|
Loading…
Reference in New Issue
Block a user