From ffc75252a50a303f04f83f744b1a2a20ddfdad48 Mon Sep 17 00:00:00 2001 From: Frederik Gossen Date: Fri, 3 May 2019 20:21:28 +0200 Subject: [PATCH] [wasm-hints] Fix lazy compilation and validation in interpreter The interpreter accesses code directly from the code manager. With lazy validation, however, this code is not guaranteed to exist. The interpreter now checks for this and compiles it lazily if needed. It also handles exceptions that may arise from lazy validation. Bug: v8:9003 Change-Id: I37c365f0a4d755ed55630d01c8526f2a3efa9a9e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1594567 Reviewed-by: Clemens Hammacher Reviewed-by: Michael Starzinger Commit-Queue: Frederik Gossen Cr-Commit-Position: refs/heads/master@{#61240} --- src/wasm/wasm-interpreter.cc | 29 ++++- .../wasm/compilation-hints-interpreter.js | 113 ++++++++++++++++++ 2 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 test/mjsunit/wasm/compilation-hints-interpreter.js diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc index 516a48052a..99adfcbf74 100644 --- a/src/wasm/wasm-interpreter.cc +++ b/src/wasm/wasm-interpreter.cc @@ -20,6 +20,7 @@ #include "src/wasm/function-body-decoder-impl.h" #include "src/wasm/function-body-decoder.h" #include "src/wasm/memory-tracing.h" +#include "src/wasm/module-compiler.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-external-refs.h" #include "src/wasm/wasm-limits.h" @@ -3612,12 +3613,21 @@ class ThreadImpl { return {ExternalCallResult::EXTERNAL_RETURNED}; } - static WasmCode* GetTargetCode(WasmCodeManager* code_manager, - Address target) { + static WasmCode* GetTargetCode(Isolate* isolate, Address target) { + WasmCodeManager* code_manager = isolate->wasm_engine()->code_manager(); NativeModule* native_module = code_manager->LookupNativeModule(target); if (native_module->is_jump_table_slot(target)) { uint32_t func_index = native_module->GetFunctionIndexFromJumpTableSlot(target); + + if (!native_module->HasCode(func_index)) { + bool success = CompileLazy(isolate, native_module, func_index); + if (!success) { + DCHECK(isolate->has_pending_exception()); + return nullptr; + } + } + return native_module->GetCode(func_index); } WasmCode* code = native_module->Lookup(target); @@ -3631,8 +3641,12 @@ class ThreadImpl { ImportedFunctionEntry entry(instance_object_, function_index); Handle object_ref(entry.object_ref(), isolate_); - WasmCode* code = - GetTargetCode(isolate_->wasm_engine()->code_manager(), entry.target()); + WasmCode* code = GetTargetCode(isolate_, entry.target()); + + // In case a function's body is invalid and the function is lazily validated + // and compiled we may get an exception. + if (code == nullptr) return TryHandleException(isolate_); + FunctionSig* sig = module()->functions[function_index].sig; return CallExternalWasmFunction(isolate_, object_ref, code, sig); } @@ -3661,8 +3675,11 @@ class ThreadImpl { HandleScope handle_scope(isolate_); // Avoid leaking handles. FunctionSig* signature = module()->signatures[sig_index]; Handle object_ref = handle(entry.object_ref(), isolate_); - WasmCode* code = - GetTargetCode(isolate_->wasm_engine()->code_manager(), entry.target()); + WasmCode* code = GetTargetCode(isolate_, entry.target()); + + // In case a function's body is invalid and the function is lazily validated + // and compiled we may get an exception. + if (code == nullptr) return TryHandleException(isolate_); if (!object_ref->IsWasmInstanceObject() || /* call to an import */ !instance_object_.is_identical_to(object_ref) /* cross-instance */) { diff --git a/test/mjsunit/wasm/compilation-hints-interpreter.js b/test/mjsunit/wasm/compilation-hints-interpreter.js new file mode 100644 index 0000000000..81fddc3389 --- /dev/null +++ b/test/mjsunit/wasm/compilation-hints-interpreter.js @@ -0,0 +1,113 @@ +// Copyright 2019 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: --experimental-wasm-compilation-hints --wasm-lazy-validation + +load('test/mjsunit/wasm/wasm-module-builder.js'); + +(function testInterpreterCallsLazyFunctionInOtherInstance() { + print(arguments.callee.name); + let builder0 = new WasmModuleBuilder(); + builder0.addFunction("getX", kSig_i_v) + .addBody([kExprI32Const, 42]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierBaseline, + kCompilationHintTierBaseline) + .exportFunc(); + let builder1 = new WasmModuleBuilder(); + builder1.addImport("otherModule", "getX", kSig_i_v); + builder1.addFunction("plusX", kSig_i_i) + .addBody([kExprCallFunction, 0, + kExprGetLocal, 0, + kExprI32Add]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter) + .exportFunc(); + let instance0 = builder0.instantiate(); + let instance1 = builder1.instantiate( + {otherModule: {getX: instance0.exports.getX}}); + assertEquals(46, instance1.exports.plusX(4)); +})(); + +(function testInterpreterCallsLazyBadFunctionInOtherInstance() { + print(arguments.callee.name); + let builder0 = new WasmModuleBuilder(); + builder0.addFunction("getX", kSig_i_v) + .addBody([kExprI64Const, 42]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierBaseline, + kCompilationHintTierBaseline) + .exportFunc(); + let builder1 = new WasmModuleBuilder(); + builder1.addImport("otherModule", "getX", kSig_i_v); + builder1.addFunction("plusX", kSig_i_i) + .addBody([kExprCallFunction, 0, + kExprGetLocal, 0, + kExprI32Add]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter) + .exportFunc(); + let instance0 = builder0.instantiate(); + let instance1 = builder1.instantiate( + {otherModule: {getX: instance0.exports.getX}}); + assertThrows(() => instance1.exports.plusX(4), + WebAssembly.CompileError, + "Compiling function #0:\"getX\" failed: type error in " + + "merge[0] (expected i32, got i64) @+57"); +})(); + +(function testInterpreterCallsLazyFunctionThroughIndirection() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let add = builder.addFunction('add', sig_i_ii) + .addBody([kExprGetLocal, 0, + kExprGetLocal, 1, + kExprI32Add]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter); + builder.appendToTable([add.index]); + builder.addFunction('main', kSig_i_iii) + .addBody([// Call indirect #0 with args <#1, #2>. + kExprGetLocal, 1, + kExprGetLocal, 2, + kExprGetLocal, 0, + kExprCallIndirect, sig_i_ii, kTableZero]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter) + .exportFunc(); + assertEquals(99, builder.instantiate().exports.main(0, 22, 77)); +})(); + +(function testInterpreterCallsLazyBadFunctionThroughIndirection() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let add = builder.addFunction('add', sig_i_ii) + .addBody([kExprGetLocal, 0, + kExprGetLocal, 1, + kExprI64Add]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter); + builder.appendToTable([add.index]); + builder.addFunction('main', kSig_i_iii) + .addBody([// Call indirect #0 with args <#1, #2>. + kExprGetLocal, 1, + kExprGetLocal, 2, + kExprGetLocal, 0, + kExprCallIndirect, sig_i_ii, kTableZero]) + .setCompilationHint(kCompilationHintStrategyLazy, + kCompilationHintTierInterpreter, + kCompilationHintTierInterpreter) + .exportFunc(); + assertThrows(() => builder.instantiate().exports.main(0, 22, 77), + WebAssembly.CompileError, + "Compiling function #0:\"add\" failed: i64.add[1] expected " + + "type i64, found get_local of type i32 @+83"); +})();