[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:
Clemens Backes 2020-04-24 17:51:23 +02:00 committed by Commit Bot
parent bce121c7c5
commit 1fb1db1792
11 changed files with 0 additions and 663 deletions

View File

@ -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());

View File

@ -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) \

View File

@ -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,

View File

@ -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

View File

@ -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",

View File

@ -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],

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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));
})();

View File

@ -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();
})();