Commit 1fb1db17 authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[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: 's avatarThibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67427}
parent bce121c7
......@@ -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],
......
This diff is collapsed.
......@@ -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.
......
// 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);
// 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));
})();
// 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();
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment