72cfc5c638
This CL adds a new PAYLOAD_SIZEOF specification to runtime_offsets_list.h which defines InstanceSize methods given an method name to invoke to get the header size (i.e., the size of the object portion before the payload). It uses this new specification to create appropriate InstanceSize() methods for objects written to read-only sections of snapshots, instead of needing separate size calculations for SIMARM_X64. It adds more methods to Instructions to avoid special casing for bare instructions mode. It also removes the special casing for SIMARM_X64, serializing all read-only objects in the same manner even when not in a crossword situation. Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-mac-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-precomp-win-release-x64-try Change-Id: Ie3e4009f4bc03688998c32281e42fa22a255731d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/165501 Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
564 lines
21 KiB
C++
564 lines
21 KiB
C++
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/compiler/relocation.h"
|
|
|
|
#include "vm/code_patcher.h"
|
|
#include "vm/heap/pages.h"
|
|
#include "vm/instructions.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/stub_code.h"
|
|
|
|
namespace dart {
|
|
|
|
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
|
|
// Only for testing.
|
|
DEFINE_FLAG(bool,
|
|
always_generate_trampolines_for_testing,
|
|
false,
|
|
"Generate always trampolines (for testing purposes).");
|
|
|
|
const intptr_t kTrampolineSize =
|
|
Utils::RoundUp(PcRelativeTrampolineJumpPattern::kLengthInBytes,
|
|
compiler::target::Instructions::kBarePayloadAlignment);
|
|
|
|
CodeRelocator::CodeRelocator(Thread* thread,
|
|
GrowableArray<CodePtr>* code_objects,
|
|
GrowableArray<ImageWriterCommand>* commands)
|
|
: StackResource(thread),
|
|
thread_(thread),
|
|
code_objects_(code_objects),
|
|
commands_(commands),
|
|
kind_type_and_offset_(Smi::Handle(thread->zone())),
|
|
target_(Object::Handle(thread->zone())),
|
|
destination_(Code::Handle(thread->zone())) {}
|
|
|
|
void CodeRelocator::Relocate(bool is_vm_isolate) {
|
|
Zone* zone = Thread::Current()->zone();
|
|
auto& current_caller = Code::Handle(zone);
|
|
auto& call_targets = Array::Handle(zone);
|
|
|
|
// Do one linear pass over all code objects and determine:
|
|
//
|
|
// * the maximum instruction size
|
|
// * the maximum number of calls
|
|
// * the maximum offset into a target instruction
|
|
//
|
|
FindInstructionAndCallLimits();
|
|
|
|
// Emit all instructions and do relocations on the way.
|
|
for (intptr_t i = 0; i < code_objects_->length(); ++i) {
|
|
current_caller = (*code_objects_)[i];
|
|
|
|
const intptr_t code_text_offset = next_text_offset_;
|
|
if (!AddInstructionsToText(current_caller.raw())) {
|
|
continue;
|
|
}
|
|
|
|
call_targets = current_caller.static_calls_target_table();
|
|
ScanCallTargets(current_caller, call_targets, code_text_offset);
|
|
|
|
// Any unresolved calls to this instruction can be fixed now.
|
|
ResolveUnresolvedCallsTargeting(current_caller.instructions());
|
|
|
|
// If we have forward/backwards calls which are almost out-of-range, we'll
|
|
// create trampolines now.
|
|
BuildTrampolinesForAlmostOutOfRangeCalls();
|
|
}
|
|
|
|
// We're guaranteed to have all calls resolved, since
|
|
// * backwards calls are resolved eagerly
|
|
// * forward calls are resolved once the target is written
|
|
ASSERT(all_unresolved_calls_.IsEmpty());
|
|
ASSERT(unresolved_calls_by_destination_.IsEmpty());
|
|
|
|
// Any trampolines we created must be patched with the right offsets.
|
|
auto it = trampolines_by_destination_.GetIterator();
|
|
while (true) {
|
|
auto entry = it.Next();
|
|
if (entry == nullptr) break;
|
|
|
|
UnresolvedTrampolineList* trampoline_list = entry->value;
|
|
while (!trampoline_list->IsEmpty()) {
|
|
auto unresolved_trampoline = trampoline_list->RemoveFirst();
|
|
ResolveTrampoline(unresolved_trampoline);
|
|
delete unresolved_trampoline;
|
|
}
|
|
delete trampoline_list;
|
|
}
|
|
trampolines_by_destination_.Clear();
|
|
|
|
// Don't drop static call targets table yet. Snapshotter will skip it anyway
|
|
// however we might need it to write information into V8 snapshot profile.
|
|
}
|
|
|
|
void CodeRelocator::FindInstructionAndCallLimits() {
|
|
auto zone = thread_->zone();
|
|
auto& current_caller = Code::Handle(zone);
|
|
auto& call_targets = Array::Handle(zone);
|
|
|
|
for (intptr_t i = 0; i < code_objects_->length(); ++i) {
|
|
current_caller = (*code_objects_)[i];
|
|
const intptr_t size =
|
|
ImageWriter::SizeInSnapshot(current_caller.instructions());
|
|
if (size > max_instructions_size_) {
|
|
max_instructions_size_ = size;
|
|
}
|
|
|
|
call_targets = current_caller.static_calls_target_table();
|
|
if (!call_targets.IsNull()) {
|
|
intptr_t num_calls = 0;
|
|
StaticCallsTable calls(call_targets);
|
|
for (auto call : calls) {
|
|
kind_type_and_offset_ = call.Get<Code::kSCallTableKindAndOffset>();
|
|
const auto kind =
|
|
Code::KindField::decode(kind_type_and_offset_.Value());
|
|
const auto return_pc_offset =
|
|
Code::OffsetField::decode(kind_type_and_offset_.Value());
|
|
const auto call_entry_point =
|
|
Code::EntryPointField::decode(kind_type_and_offset_.Value());
|
|
|
|
if (kind == Code::kCallViaCode) {
|
|
continue;
|
|
}
|
|
|
|
destination_ = GetTarget(call);
|
|
num_calls++;
|
|
|
|
// A call site can decide to jump not to the beginning of a function but
|
|
// rather jump into it at a certain (positive) offset.
|
|
int32_t offset_into_target = 0;
|
|
if (kind == Code::kPcRelativeCall || kind == Code::kPcRelativeTTSCall) {
|
|
const intptr_t call_instruction_offset =
|
|
return_pc_offset - PcRelativeCallPattern::kLengthInBytes;
|
|
PcRelativeCallPattern call(current_caller.PayloadStart() +
|
|
call_instruction_offset);
|
|
ASSERT(call.IsValid());
|
|
offset_into_target = call.distance();
|
|
} else {
|
|
ASSERT(kind == Code::kPcRelativeTailCall);
|
|
const intptr_t call_instruction_offset =
|
|
return_pc_offset - PcRelativeTailCallPattern::kLengthInBytes;
|
|
PcRelativeTailCallPattern call(current_caller.PayloadStart() +
|
|
call_instruction_offset);
|
|
ASSERT(call.IsValid());
|
|
offset_into_target = call.distance();
|
|
}
|
|
|
|
const uword destination_payload = destination_.PayloadStart();
|
|
const uword entry_point = call_entry_point == Code::kUncheckedEntry
|
|
? destination_.UncheckedEntryPoint()
|
|
: destination_.EntryPoint();
|
|
|
|
offset_into_target += (entry_point - destination_payload);
|
|
|
|
if (offset_into_target > max_offset_into_target_) {
|
|
max_offset_into_target_ = offset_into_target;
|
|
}
|
|
}
|
|
|
|
if (num_calls > max_calls_) {
|
|
max_calls_ = num_calls;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CodeRelocator::AddInstructionsToText(CodePtr code) {
|
|
InstructionsPtr instructions = Code::InstructionsOf(code);
|
|
|
|
// If two [Code] objects point to the same [Instructions] object, we'll just
|
|
// use the first one (they are equivalent for all practical purposes).
|
|
if (text_offsets_.HasKey(instructions)) {
|
|
return false;
|
|
}
|
|
text_offsets_.Insert({instructions, next_text_offset_});
|
|
commands_->Add(ImageWriterCommand(next_text_offset_, code));
|
|
next_text_offset_ += ImageWriter::SizeInSnapshot(instructions);
|
|
|
|
return true;
|
|
}
|
|
|
|
UnresolvedTrampoline* CodeRelocator::FindTrampolineFor(
|
|
UnresolvedCall* unresolved_call) {
|
|
auto destination = Code::InstructionsOf(unresolved_call->callee);
|
|
auto entry = trampolines_by_destination_.Lookup(destination);
|
|
if (entry != nullptr) {
|
|
UnresolvedTrampolineList* trampolines = entry->value;
|
|
ASSERT(!trampolines->IsEmpty());
|
|
|
|
// For the destination of [unresolved_call] we might have multiple
|
|
// trampolines. The trampolines are sorted according to insertion order,
|
|
// which guarantees increasing text_offset's. So we go from the back of the
|
|
// list as long as we have trampolines that are in-range and then check
|
|
// whether the target offset matches.
|
|
auto it = trampolines->End();
|
|
--it;
|
|
do {
|
|
UnresolvedTrampoline* trampoline = *it;
|
|
if (!IsTargetInRangeFor(unresolved_call, trampoline->text_offset)) {
|
|
break;
|
|
}
|
|
if (trampoline->offset_into_target ==
|
|
unresolved_call->offset_into_target) {
|
|
return trampoline;
|
|
}
|
|
--it;
|
|
} while (it != trampolines->Begin());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void CodeRelocator::AddTrampolineToText(InstructionsPtr destination,
|
|
uint8_t* trampoline_bytes,
|
|
intptr_t trampoline_length) {
|
|
commands_->Add(ImageWriterCommand(next_text_offset_, trampoline_bytes,
|
|
trampoline_length));
|
|
next_text_offset_ += trampoline_length;
|
|
}
|
|
|
|
void CodeRelocator::ScanCallTargets(const Code& code,
|
|
const Array& call_targets,
|
|
intptr_t code_text_offset) {
|
|
if (call_targets.IsNull()) {
|
|
return;
|
|
}
|
|
StaticCallsTable calls(call_targets);
|
|
for (auto call : calls) {
|
|
kind_type_and_offset_ = call.Get<Code::kSCallTableKindAndOffset>();
|
|
const auto kind = Code::KindField::decode(kind_type_and_offset_.Value());
|
|
const auto return_pc_offset =
|
|
Code::OffsetField::decode(kind_type_and_offset_.Value());
|
|
const auto call_entry_point =
|
|
Code::EntryPointField::decode(kind_type_and_offset_.Value());
|
|
|
|
if (kind == Code::kCallViaCode) {
|
|
continue;
|
|
}
|
|
|
|
destination_ = GetTarget(call);
|
|
|
|
// A call site can decide to jump not to the beginning of a function but
|
|
// rather jump into it at a certain offset.
|
|
int32_t offset_into_target = 0;
|
|
bool is_tail_call;
|
|
intptr_t call_instruction_offset;
|
|
if (kind == Code::kPcRelativeCall || kind == Code::kPcRelativeTTSCall) {
|
|
call_instruction_offset =
|
|
return_pc_offset - PcRelativeCallPattern::kLengthInBytes;
|
|
PcRelativeCallPattern call(code.PayloadStart() + call_instruction_offset);
|
|
ASSERT(call.IsValid());
|
|
offset_into_target = call.distance();
|
|
is_tail_call = false;
|
|
} else {
|
|
ASSERT(kind == Code::kPcRelativeTailCall);
|
|
call_instruction_offset =
|
|
return_pc_offset - PcRelativeTailCallPattern::kLengthInBytes;
|
|
PcRelativeTailCallPattern call(code.PayloadStart() +
|
|
call_instruction_offset);
|
|
ASSERT(call.IsValid());
|
|
offset_into_target = call.distance();
|
|
is_tail_call = true;
|
|
}
|
|
|
|
const uword destination_payload = destination_.PayloadStart();
|
|
const uword entry_point = call_entry_point == Code::kUncheckedEntry
|
|
? destination_.UncheckedEntryPoint()
|
|
: destination_.EntryPoint();
|
|
|
|
offset_into_target += (entry_point - destination_payload);
|
|
|
|
const intptr_t text_offset =
|
|
code_text_offset + AdjustPayloadOffset(call_instruction_offset);
|
|
|
|
UnresolvedCall unresolved_call(code.raw(), call_instruction_offset,
|
|
text_offset, destination_.raw(),
|
|
offset_into_target, is_tail_call);
|
|
if (!TryResolveBackwardsCall(&unresolved_call)) {
|
|
EnqueueUnresolvedCall(new UnresolvedCall(unresolved_call));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeRelocator::EnqueueUnresolvedCall(UnresolvedCall* unresolved_call) {
|
|
// Add it to the min-heap by .text offset.
|
|
all_unresolved_calls_.Append(unresolved_call);
|
|
|
|
// Add it to callers of destination.
|
|
InstructionsPtr destination = Code::InstructionsOf(unresolved_call->callee);
|
|
if (!unresolved_calls_by_destination_.HasKey(destination)) {
|
|
unresolved_calls_by_destination_.Insert(
|
|
{destination, new SameDestinationUnresolvedCallsList()});
|
|
}
|
|
unresolved_calls_by_destination_.LookupValue(destination)
|
|
->Append(unresolved_call);
|
|
}
|
|
|
|
void CodeRelocator::EnqueueUnresolvedTrampoline(
|
|
UnresolvedTrampoline* unresolved_trampoline) {
|
|
auto destination = Code::InstructionsOf(unresolved_trampoline->callee);
|
|
auto entry = trampolines_by_destination_.Lookup(destination);
|
|
|
|
UnresolvedTrampolineList* trampolines = nullptr;
|
|
if (entry == nullptr) {
|
|
trampolines = new UnresolvedTrampolineList();
|
|
trampolines_by_destination_.Insert({destination, trampolines});
|
|
} else {
|
|
trampolines = entry->value;
|
|
}
|
|
trampolines->Append(unresolved_trampoline);
|
|
}
|
|
|
|
bool CodeRelocator::TryResolveBackwardsCall(UnresolvedCall* unresolved_call) {
|
|
auto callee = Code::InstructionsOf(unresolved_call->callee);
|
|
auto map_entry = text_offsets_.Lookup(callee);
|
|
if (map_entry == nullptr) return false;
|
|
|
|
ResolveCall(unresolved_call);
|
|
return true;
|
|
}
|
|
|
|
void CodeRelocator::ResolveUnresolvedCallsTargeting(
|
|
const InstructionsPtr instructions) {
|
|
if (unresolved_calls_by_destination_.HasKey(instructions)) {
|
|
SameDestinationUnresolvedCallsList* calls =
|
|
unresolved_calls_by_destination_.LookupValue(instructions);
|
|
auto it = calls->Begin();
|
|
while (it != calls->End()) {
|
|
UnresolvedCall* unresolved_call = *it;
|
|
++it;
|
|
ASSERT(Code::InstructionsOf(unresolved_call->callee) == instructions);
|
|
ResolveCall(unresolved_call);
|
|
|
|
// Remove the call from both lists.
|
|
calls->Remove(unresolved_call);
|
|
all_unresolved_calls_.Remove(unresolved_call);
|
|
|
|
delete unresolved_call;
|
|
}
|
|
ASSERT(calls->IsEmpty());
|
|
delete calls;
|
|
bool ok = unresolved_calls_by_destination_.Remove(instructions);
|
|
ASSERT(ok);
|
|
}
|
|
}
|
|
|
|
void CodeRelocator::ResolveCall(UnresolvedCall* unresolved_call) {
|
|
const intptr_t destination_text =
|
|
FindDestinationInText(Code::InstructionsOf(unresolved_call->callee),
|
|
unresolved_call->offset_into_target);
|
|
|
|
ResolveCallToDestination(unresolved_call, destination_text);
|
|
}
|
|
|
|
void CodeRelocator::ResolveCallToDestination(UnresolvedCall* unresolved_call,
|
|
intptr_t destination_text) {
|
|
const intptr_t call_text_offset = unresolved_call->text_offset;
|
|
const intptr_t call_offset = unresolved_call->call_offset;
|
|
|
|
const int32_t distance = destination_text - call_text_offset;
|
|
{
|
|
auto const caller = unresolved_call->caller;
|
|
uword addr = Code::PayloadStartOf(caller) + call_offset;
|
|
if (FLAG_write_protect_code) {
|
|
addr -= OldPage::Of(Code::InstructionsOf(caller))->AliasOffset();
|
|
}
|
|
if (unresolved_call->is_tail_call) {
|
|
PcRelativeTailCallPattern call(addr);
|
|
ASSERT(call.IsValid());
|
|
call.set_distance(static_cast<int32_t>(distance));
|
|
ASSERT(call.distance() == distance);
|
|
} else {
|
|
PcRelativeCallPattern call(addr);
|
|
ASSERT(call.IsValid());
|
|
call.set_distance(static_cast<int32_t>(distance));
|
|
ASSERT(call.distance() == distance);
|
|
}
|
|
}
|
|
|
|
unresolved_call->caller = nullptr;
|
|
unresolved_call->callee = nullptr;
|
|
}
|
|
|
|
void CodeRelocator::ResolveTrampoline(
|
|
UnresolvedTrampoline* unresolved_trampoline) {
|
|
const intptr_t trampoline_text_offset = unresolved_trampoline->text_offset;
|
|
const uword trampoline_start =
|
|
reinterpret_cast<uword>(unresolved_trampoline->trampoline_bytes);
|
|
|
|
auto callee = Code::InstructionsOf(unresolved_trampoline->callee);
|
|
auto destination_text =
|
|
FindDestinationInText(callee, unresolved_trampoline->offset_into_target);
|
|
const int32_t distance = destination_text - trampoline_text_offset;
|
|
|
|
PcRelativeTrampolineJumpPattern pattern(trampoline_start);
|
|
pattern.Initialize();
|
|
pattern.set_distance(distance);
|
|
ASSERT(pattern.distance() == distance);
|
|
}
|
|
|
|
bool CodeRelocator::IsTargetInRangeFor(UnresolvedCall* unresolved_call,
|
|
intptr_t target_text_offset) {
|
|
const auto forward_distance =
|
|
target_text_offset - unresolved_call->text_offset;
|
|
if (unresolved_call->is_tail_call) {
|
|
return PcRelativeTailCallPattern::kLowerCallingRange < forward_distance &&
|
|
forward_distance < PcRelativeTailCallPattern::kUpperCallingRange;
|
|
} else {
|
|
return PcRelativeCallPattern::kLowerCallingRange < forward_distance &&
|
|
forward_distance < PcRelativeCallPattern::kUpperCallingRange;
|
|
}
|
|
}
|
|
|
|
CodePtr CodeRelocator::GetTarget(const StaticCallsTableEntry& call) {
|
|
// The precompiler should have already replaced all function entries
|
|
// with code entries.
|
|
ASSERT(call.Get<Code::kSCallTableFunctionTarget>() == Function::null());
|
|
|
|
target_ = call.Get<Code::kSCallTableCodeOrTypeTarget>();
|
|
if (target_.IsAbstractType()) {
|
|
target_ = AbstractType::Cast(target_).type_test_stub();
|
|
destination_ = Code::Cast(target_).raw();
|
|
|
|
// The AssertAssignableInstr will emit pc-relative calls to the TTS iff
|
|
// dst_type is instantiated. If we happened to not install an optimized
|
|
// TTS but rather a default one, it will live in the vm-isolate (to
|
|
// which we cannot make pc-relative calls).
|
|
// Though we have "equivalent" isolate-specific stubs we can use as
|
|
// targets instead.
|
|
//
|
|
// (We could make the AOT compiler install isolate-specific stubs
|
|
// into the types directly, but that does not work for types which
|
|
// live in the "vm-isolate" - such as `Type::dynamic_type()`).
|
|
if (destination_.InVMIsolateHeap()) {
|
|
auto object_store = thread_->isolate()->object_store();
|
|
|
|
if (destination_.raw() == StubCode::DefaultTypeTest().raw()) {
|
|
destination_ = object_store->default_tts_stub();
|
|
} else if (destination_.raw() ==
|
|
StubCode::DefaultNullableTypeTest().raw()) {
|
|
destination_ = object_store->default_nullable_tts_stub();
|
|
} else if (destination_.raw() == StubCode::TopTypeTypeTest().raw()) {
|
|
destination_ = object_store->top_type_tts_stub();
|
|
} else if (destination_.raw() == StubCode::UnreachableTypeTest().raw()) {
|
|
destination_ = object_store->unreachable_tts_stub();
|
|
} else if (destination_.raw() == StubCode::SlowTypeTest().raw()) {
|
|
destination_ = object_store->slow_tts_stub();
|
|
} else if (destination_.raw() ==
|
|
StubCode::NullableTypeParameterTypeTest().raw()) {
|
|
destination_ = object_store->nullable_type_parameter_tts_stub();
|
|
} else if (destination_.raw() ==
|
|
StubCode::TypeParameterTypeTest().raw()) {
|
|
destination_ = object_store->type_parameter_tts_stub();
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
} else {
|
|
ASSERT(target_.IsCode());
|
|
destination_ = Code::Cast(target_).raw();
|
|
}
|
|
ASSERT(!destination_.InVMIsolateHeap());
|
|
return destination_.raw();
|
|
}
|
|
|
|
void CodeRelocator::BuildTrampolinesForAlmostOutOfRangeCalls() {
|
|
while (!all_unresolved_calls_.IsEmpty()) {
|
|
UnresolvedCall* unresolved_call = all_unresolved_calls_.First();
|
|
|
|
// If we can emit another instructions object without causing the unresolved
|
|
// forward calls to become out-of-range, we'll not resolve it yet (maybe the
|
|
// target function will come very soon and we don't need a trampoline at
|
|
// all).
|
|
const intptr_t future_boundary =
|
|
next_text_offset_ + max_instructions_size_ +
|
|
kTrampolineSize *
|
|
(unresolved_calls_by_destination_.Length() + max_calls_);
|
|
if (IsTargetInRangeFor(unresolved_call, future_boundary) &&
|
|
!FLAG_always_generate_trampolines_for_testing) {
|
|
break;
|
|
}
|
|
|
|
// We have a "critical" [unresolved_call] we have to resolve. If an
|
|
// existing trampoline is in range, we use that otherwise we create a new
|
|
// trampoline.
|
|
|
|
// In the worst case we'll make a new trampoline here, in which case the
|
|
// current text offset must be in range for the "critical"
|
|
// [unresolved_call].
|
|
ASSERT(IsTargetInRangeFor(unresolved_call, next_text_offset_));
|
|
|
|
// See if there is already a trampoline we could use.
|
|
intptr_t trampoline_text_offset = -1;
|
|
auto callee = Code::InstructionsOf(unresolved_call->callee);
|
|
|
|
if (!FLAG_always_generate_trampolines_for_testing) {
|
|
auto old_trampoline_entry = FindTrampolineFor(unresolved_call);
|
|
if (old_trampoline_entry != nullptr) {
|
|
trampoline_text_offset = old_trampoline_entry->text_offset;
|
|
}
|
|
}
|
|
|
|
// If there is no trampoline yet, we'll create a new one.
|
|
if (trampoline_text_offset == -1) {
|
|
// The ownership of the trampoline bytes will be transferred to the
|
|
// [ImageWriter], which will eventually write out the bytes and delete the
|
|
// buffer.
|
|
auto trampoline_bytes = new uint8_t[kTrampolineSize];
|
|
ASSERT((kTrampolineSize % compiler::target::kWordSize) == 0);
|
|
for (uint8_t* cur = trampoline_bytes;
|
|
cur < trampoline_bytes + kTrampolineSize;
|
|
cur += compiler::target::kWordSize) {
|
|
*reinterpret_cast<compiler::target::uword*>(cur) =
|
|
kBreakInstructionFiller;
|
|
}
|
|
auto unresolved_trampoline = new UnresolvedTrampoline{
|
|
unresolved_call->callee,
|
|
unresolved_call->offset_into_target,
|
|
trampoline_bytes,
|
|
next_text_offset_,
|
|
};
|
|
AddTrampolineToText(callee, trampoline_bytes, kTrampolineSize);
|
|
EnqueueUnresolvedTrampoline(unresolved_trampoline);
|
|
trampoline_text_offset = unresolved_trampoline->text_offset;
|
|
}
|
|
|
|
// Let the unresolved call to [destination] jump to the trampoline
|
|
// instead.
|
|
auto destination = Code::InstructionsOf(unresolved_call->callee);
|
|
ResolveCallToDestination(unresolved_call, trampoline_text_offset);
|
|
|
|
// Remove this unresolved call from the global list and the per-destination
|
|
// list.
|
|
auto calls = unresolved_calls_by_destination_.LookupValue(destination);
|
|
calls->Remove(unresolved_call);
|
|
all_unresolved_calls_.Remove(unresolved_call);
|
|
delete unresolved_call;
|
|
|
|
// If this destination has no longer any unresolved calls, remove it.
|
|
if (calls->IsEmpty()) {
|
|
unresolved_calls_by_destination_.Remove(destination);
|
|
delete calls;
|
|
}
|
|
}
|
|
}
|
|
|
|
intptr_t CodeRelocator::FindDestinationInText(const InstructionsPtr destination,
|
|
intptr_t offset_into_target) {
|
|
auto const destination_offset = text_offsets_.LookupValue(destination);
|
|
return destination_offset + AdjustPayloadOffset(offset_into_target);
|
|
}
|
|
|
|
intptr_t CodeRelocator::AdjustPayloadOffset(intptr_t payload_offset) {
|
|
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
|
return payload_offset;
|
|
}
|
|
return compiler::target::Instructions::HeaderSize() + payload_offset;
|
|
}
|
|
|
|
#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
|
|
} // namespace dart
|