[wasm] Prepare for multiple jump tables

This adds logic to choose the closest jump table for each call in wasm
code. The "main jump table" (held in {NativeModule::main_jump_table_})
is still kept though and used for any external or indirect call.
Any direct call from within wasm now chooses the jump table that
corresponds to the code space that the code lives in.

R=mstarzinger@chromium.org

Bug: v8:9477
Change-Id: Ie52b5bb3a4a160cb754b8702c530f6feb182b3a9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1800576
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63760}
This commit is contained in:
Clemens Hammacher 2019-09-13 15:14:37 +02:00 committed by Commit Bot
parent b9a393a201
commit 4d97099c65
4 changed files with 42 additions and 14 deletions

View File

@ -235,7 +235,10 @@ void WasmCode::Validate() const {
switch (mode) {
case RelocInfo::WASM_CALL: {
Address target = it.rinfo()->wasm_call_address();
DCHECK(native_module_->is_jump_table_slot(target));
WasmCode* code = native_module_->Lookup(target);
CHECK_NOT_NULL(code);
CHECK_EQ(WasmCode::kJumpTable, code->kind());
CHECK(code->contains(target));
break;
}
case RelocInfo::WASM_STUB_CALL: {
@ -952,7 +955,7 @@ std::unique_ptr<WasmCode> NativeModule::AddCodeWithCodeSpace(
RelocInfo::Mode mode = it.rinfo()->rmode();
if (RelocInfo::IsWasmCall(mode)) {
uint32_t call_tag = it.rinfo()->wasm_call_tag();
Address target = GetCallTargetForFunction(call_tag);
Address target = GetNearCallTargetForFunction(call_tag, code_start);
it.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
} else if (RelocInfo::IsWasmStubCall(mode)) {
uint32_t stub_call_tag = it.rinfo()->wasm_call_tag();
@ -1306,6 +1309,21 @@ Address NativeModule::GetCallTargetForFunction(uint32_t func_index) const {
return main_jump_table_->instruction_start() + slot_offset;
}
Address NativeModule::GetNearCallTargetForFunction(uint32_t func_index,
Address near_to) const {
uint32_t slot_offset = GetJumpTableOffset(func_index);
base::MutexGuard guard(&allocation_mutex_);
for (auto& code_space_data : code_space_data_) {
const bool jump_table_reachable = !kNeedsFarJumpsBetweenCodeSpaces ||
code_space_data.region.contains(near_to);
if (jump_table_reachable && code_space_data.jump_table) {
DCHECK_LT(slot_offset, code_space_data.jump_table->instructions().size());
return code_space_data.jump_table->instruction_start() + slot_offset;
}
}
FATAL("near_to is not part of a code space");
}
Address NativeModule::GetNearRuntimeStubEntry(WasmCode::RuntimeStubId index,
Address near_to) const {
base::MutexGuard guard(&allocation_mutex_);
@ -1321,11 +1339,17 @@ Address NativeModule::GetNearRuntimeStubEntry(WasmCode::RuntimeStubId index,
uint32_t NativeModule::GetFunctionIndexFromJumpTableSlot(
Address slot_address) const {
DCHECK(is_jump_table_slot(slot_address));
uint32_t slot_offset = static_cast<uint32_t>(
slot_address - main_jump_table_->instruction_start());
WasmCodeRefScope code_refs;
WasmCode* code = Lookup(slot_address);
DCHECK_NOT_NULL(code);
DCHECK_EQ(WasmCode::kJumpTable, code->kind());
uint32_t slot_offset =
static_cast<uint32_t>(slot_address - code->instruction_start());
uint32_t slot_idx = JumpTableAssembler::SlotOffsetToIndex(slot_offset);
DCHECK_LT(slot_idx, module_->num_declared_functions);
DCHECK_EQ(slot_address,
code->instruction_start() +
JumpTableAssembler::JumpSlotIndexToOffset(slot_idx));
return module_->num_imported_functions + slot_idx;
}

View File

@ -345,6 +345,8 @@ class WasmCodeAllocator {
bool is_executable_ = false;
// TODO(clemensh): Remove this field once multiple code spaces are supported
// everywhere.
const bool can_request_more_memory_;
std::shared_ptr<Counters> async_counters_;
@ -412,22 +414,23 @@ class V8_EXPORT_PRIVATE NativeModule final {
uint32_t GetJumpTableOffset(uint32_t func_index) const;
bool is_jump_table_slot(Address address) const {
return main_jump_table_->contains(address);
}
// Returns the canonical target to call for the given function (the slot in
// the first jump table).
Address GetCallTargetForFunction(uint32_t func_index) const;
// Similarly to {GetCallTargetForFunction}, but ensures that the returned
// address is near to the {near_to} address by finding the closest jump table.
Address GetNearCallTargetForFunction(uint32_t func_index,
Address near_to) const;
// Get a runtime stub entry (which is a far jump table slot) within near-call
// distance to {near_to}. Fails if {near_to} is not part of any code space of
// this module.
Address GetNearRuntimeStubEntry(WasmCode::RuntimeStubId index,
Address near_to) const;
// Reverse lookup from a given call target (i.e. a jump table slot as the
// above {GetCallTargetForFunction} returns) to a function index.
// Reverse lookup from a given call target (which must be a jump table slot)
// to a function index.
uint32_t GetFunctionIndexFromJumpTableSlot(Address slot_address) const;
bool SetExecutable(bool executable) {

View File

@ -3779,7 +3779,8 @@ class ThreadImpl {
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)) {
WasmCode* code = native_module->Lookup(target);
if (code->kind() == WasmCode::kJumpTable) {
uint32_t func_index =
native_module->GetFunctionIndexFromJumpTableSlot(target);
@ -3793,7 +3794,6 @@ class ThreadImpl {
return native_module->GetCode(func_index);
}
WasmCode* code = native_module->Lookup(target);
DCHECK_EQ(code->instruction_start(), target);
return code;
}

View File

@ -548,7 +548,8 @@ bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
switch (mode) {
case RelocInfo::WASM_CALL: {
uint32_t tag = GetWasmCalleeTag(iter.rinfo());
Address target = native_module_->GetCallTargetForFunction(tag);
Address target = native_module_->GetNearCallTargetForFunction(
tag, code->instruction_start());
iter.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH);
break;
}