Commit 2b897275 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Preserve interpreter entry even on tier-up.

This makes sure that a tier-up from Ignition to TurboFan (or any other
code publishing) preserves redirections to the Interpreter. Currently an
interpreted function never switches back to compiled.

R=titzer@chromium.org
TEST=mjsunit/wasm/interpreter-mixed
BUG=v8:7921,v8:8018

Change-Id: Ifca479953509708c998c11cc00b481c232678e00
Reviewed-on: https://chromium-review.googlesource.com/1179661
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55195}
parent 606fcce2
...@@ -1020,7 +1020,7 @@ RUNTIME_FUNCTION(Runtime_RedirectToWasmInterpreter) { ...@@ -1020,7 +1020,7 @@ RUNTIME_FUNCTION(Runtime_RedirectToWasmInterpreter) {
} }
RUNTIME_FUNCTION(Runtime_WasmTraceMemory) { RUNTIME_FUNCTION(Runtime_WasmTraceMemory) {
HandleScope hs(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_CHECKED(Smi, info_addr, 0); CONVERT_ARG_CHECKED(Smi, info_addr, 0);
...@@ -1048,8 +1048,21 @@ RUNTIME_FUNCTION(Runtime_WasmTraceMemory) { ...@@ -1048,8 +1048,21 @@ RUNTIME_FUNCTION(Runtime_WasmTraceMemory) {
return ReadOnlyRoots(isolate).undefined_value(); return ReadOnlyRoots(isolate).undefined_value();
} }
RUNTIME_FUNCTION(Runtime_WasmTierUpFunction) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
CONVERT_SMI_ARG_CHECKED(function_index, 1);
if (!isolate->wasm_engine()->CompileFunction(
isolate, instance->module_object()->native_module(), function_index,
wasm::WasmEngine::kOptimizedTier)) {
return ReadOnlyRoots(isolate).exception();
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) { RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
HandleScope shs(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
CHECK(WasmExportedFunction::IsWasmExportedFunction(*function)); CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
......
...@@ -522,6 +522,7 @@ namespace internal { ...@@ -522,6 +522,7 @@ namespace internal {
F(InNewSpace, 1, 1) \ F(InNewSpace, 1, 1) \
F(IsAsmWasmCode, 1, 1) \ F(IsAsmWasmCode, 1, 1) \
F(IsConcurrentRecompilationSupported, 0, 1) \ F(IsConcurrentRecompilationSupported, 0, 1) \
F(WasmTierUpFunction, 2, 1) \
F(IsLiftoffFunction, 1, 1) \ F(IsLiftoffFunction, 1, 1) \
F(IsWasmCode, 1, 1) \ F(IsWasmCode, 1, 1) \
F(IsWasmTrapHandlerEnabled, 0, 1) \ F(IsWasmTrapHandlerEnabled, 0, 1) \
......
...@@ -408,7 +408,9 @@ WasmCode* NativeModule::AddCodeCopy(Handle<Code> code, WasmCode::Kind kind, ...@@ -408,7 +408,9 @@ WasmCode* NativeModule::AddCodeCopy(Handle<Code> code, WasmCode::Kind kind,
WasmCode* NativeModule::AddInterpreterEntry(Handle<Code> code, uint32_t index) { WasmCode* NativeModule::AddInterpreterEntry(Handle<Code> code, uint32_t index) {
WasmCode* ret = AddAnonymousCode(code, WasmCode::kInterpreterEntry); WasmCode* ret = AddAnonymousCode(code, WasmCode::kInterpreterEntry);
ret->index_ = Just(index); ret->index_ = Just(index);
base::LockGuard<base::Mutex> lock(&allocation_mutex_);
PatchJumpTable(index, ret->instruction_start(), WasmCode::kFlushICache); PatchJumpTable(index, ret->instruction_start(), WasmCode::kFlushICache);
set_code(index, ret);
return ret; return ret;
} }
...@@ -572,9 +574,13 @@ WasmCode* NativeModule::AddDeserializedCode( ...@@ -572,9 +574,13 @@ WasmCode* NativeModule::AddDeserializedCode(
} }
void NativeModule::PublishCode(WasmCode* code) { void NativeModule::PublishCode(WasmCode* code) {
// TODO(clemensh): Remove the need for locking here. Probably requires
// word-aligning the jump table slots.
base::LockGuard<base::Mutex> lock(&allocation_mutex_); base::LockGuard<base::Mutex> lock(&allocation_mutex_);
// Skip publishing code if there is an active redirection to the interpreter
// for the given function index, in order to preserve the redirection.
if (has_code(code->index()) &&
this->code(code->index())->kind() == WasmCode::kInterpreterEntry) {
return;
}
if (!code->protected_instructions_.is_empty()) { if (!code->protected_instructions_.is_empty()) {
code->RegisterTrapHandlerData(); code->RegisterTrapHandlerData();
} }
......
...@@ -302,8 +302,6 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -302,8 +302,6 @@ class V8_EXPORT_PRIVATE NativeModule final {
return jump_table_->contains(address); return jump_table_->contains(address);
} }
uint32_t GetFunctionIndexFromJumpTableSlot(Address slot_address) const;
// Transition this module from code relying on trap handlers (i.e. without // Transition this module from code relying on trap handlers (i.e. without
// explicit memory bounds checks) to code that does not require trap handlers // explicit memory bounds checks) to code that does not require trap handlers
// (i.e. code with explicit bounds checks). // (i.e. code with explicit bounds checks).
...@@ -316,6 +314,10 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -316,6 +314,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
// slot within {jump_table_}). // slot within {jump_table_}).
Address GetCallTargetForFunction(uint32_t func_index) const; Address GetCallTargetForFunction(uint32_t func_index) const;
// Reverse lookup from a given call target (i.e. a jump table slot as the
// above {GetCallTargetForFunction} returns) to a function index.
uint32_t GetFunctionIndexFromJumpTableSlot(Address slot_address) const;
bool SetExecutable(bool executable); bool SetExecutable(bool executable);
// For cctests, where we build both WasmModule and the runtime objects // For cctests, where we build both WasmModule and the runtime objects
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/compilation-statistics.h" #include "src/compilation-statistics.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/objects/js-promise.h" #include "src/objects/js-promise.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/module-compiler.h" #include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h" #include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h" #include "src/wasm/streaming-decoder.h"
...@@ -167,6 +168,20 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation( ...@@ -167,6 +168,20 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
return job->CreateStreamingDecoder(); return job->CreateStreamingDecoder();
} }
bool WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
uint32_t function_index,
CompilationTier tier) {
ErrorThrower thrower(isolate, "Manually requested tier up");
WasmCompilationUnit::CompilationMode mode =
(tier == kBaselineTier) ? WasmCompilationUnit::CompilationMode::kLiftoff
: WasmCompilationUnit::CompilationMode::kTurbofan;
WasmCode* ret = WasmCompilationUnit::CompileWasmFunction(
native_module, &thrower, isolate,
GetModuleEnv(native_module->compilation_state()),
&native_module->module()->functions[function_index], mode);
return ret != nullptr;
}
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule( std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
Handle<WasmModuleObject> module_object) { Handle<WasmModuleObject> module_object) {
return module_object->managed_native_module()->get(); return module_object->managed_native_module()->get();
......
...@@ -91,6 +91,13 @@ class V8_EXPORT_PRIVATE WasmEngine { ...@@ -91,6 +91,13 @@ class V8_EXPORT_PRIVATE WasmEngine {
Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context, Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
std::shared_ptr<CompilationResultResolver> resolver); std::shared_ptr<CompilationResultResolver> resolver);
// Compiles the function with the given index at a specific compilation tier
// and returns true on success, false (and pending exception) otherwise. This
// is mostly used for testing to force a function into a specific tier.
enum CompilationTier { kBaselineTier, kOptimizedTier };
bool CompileFunction(Isolate* isolate, NativeModule* native_module,
uint32_t function_index, CompilationTier tier);
// Exports the sharable parts of the given module object so that they can be // Exports the sharable parts of the given module object so that they can be
// transferred to a different Context/Isolate using the same engine. // transferred to a different Context/Isolate using the same engine.
std::shared_ptr<NativeModule> ExportNativeModule( std::shared_ptr<NativeModule> ExportNativeModule(
......
...@@ -2318,6 +2318,7 @@ class ThreadImpl { ...@@ -2318,6 +2318,7 @@ class ThreadImpl {
uint32_t entry_index = Pop().to<uint32_t>(); uint32_t entry_index = Pop().to<uint32_t>();
// Assume only one table for now. // Assume only one table for now.
DCHECK_LE(module()->tables.size(), 1u); DCHECK_LE(module()->tables.size(), 1u);
CommitPc(pc); // TODO(wasm): Be more disciplined about committing PC.
ExternalCallResult result = ExternalCallResult result =
CallIndirectFunction(0, entry_index, imm.sig_index); CallIndirectFunction(0, entry_index, imm.sig_index);
switch (result.type) { switch (result.type) {
......
...@@ -193,3 +193,27 @@ function redirectToInterpreter( ...@@ -193,3 +193,27 @@ function redirectToInterpreter(
} }
} }
})(); })();
(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));
})();
...@@ -310,7 +310,7 @@ function checkStack(stack, expected_lines) { ...@@ -310,7 +310,7 @@ function checkStack(stack, expected_lines) {
if (!(e instanceof TypeError)) throw e; if (!(e instanceof TypeError)) throw e;
checkStack(stripPath(e.stack), [ checkStack(stripPath(e.stack), [
'TypeError: ' + kTrapMsgs[kTrapTypeError], // - 'TypeError: ' + kTrapMsgs[kTrapTypeError], // -
' at indirect (wasm-function[2]:1)', // - ' at indirect (wasm-function[2]:3)', // -
' at main (wasm-function[3]:3)', // - ' at main (wasm-function[3]:3)', // -
/^ at testIllegalImports \(interpreter.js:\d+:22\)$/, // - /^ at testIllegalImports \(interpreter.js:\d+:22\)$/, // -
/^ at interpreter.js:\d+:3$/ /^ at interpreter.js:\d+:3$/
......
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