blob: 91ed48c5bfb38c41ee2db1bc536c24a48ca5a525 [file] [log] [blame]
// Copyright (c) 2018, 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/allocation.h"
#include "vm/image_snapshot.h"
#include "vm/intrusive_dlist.h"
#include "vm/object.h"
#include "vm/type_testing_stubs.h"
#include "vm/visitor.h"
namespace dart {
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_DBC) && \
// Represents a pc-relative call which has not been patched up with the final
// destination.
class UnresolvedCall : public IntrusiveDListEntry<UnresolvedCall>,
public IntrusiveDListEntry<UnresolvedCall, 2> {
UnresolvedCall(RawCode* caller,
intptr_t call_offset,
intptr_t text_offset,
RawCode* callee,
intptr_t offset_into_target)
: caller(caller),
offset_into_target(offset_into_target) {}
UnresolvedCall(const UnresolvedCall& other)
: IntrusiveDListEntry<UnresolvedCall>(),
IntrusiveDListEntry<UnresolvedCall, 2>(),
offset_into_target(other.offset_into_target) {}
// The caller which has an unresolved call.
RawCode* caller;
// The offset from the payload of the calling code which performs the call.
intptr_t call_offset;
// The offset in the .text segment where the call happens.
intptr_t text_offset;
// The target of the forward call.
RawCode* callee;
// The extra offset into the target.
intptr_t offset_into_target;
// A list of all unresolved calls.
using AllUnresolvedCallsList = IntrusiveDList<UnresolvedCall>;
// A list of all unresolved calls which call the same destination.
using SameDestinationUnresolvedCallsList = IntrusiveDList<UnresolvedCall, 2>;
// Represents a trampoline which has not been patched up with the final
// destination.
// The [CodeRelocator] will insert trampolines into the ".text" segment which
// increase the range of PC-relative calls. If a pc-relative call in normal
// code is too far away from it's destination, it will call a trampoline
// instead (which will tail-call the destination).
class UnresolvedTrampoline : public IntrusiveDListEntry<UnresolvedTrampoline> {
UnresolvedTrampoline(RawCode* callee,
intptr_t offset_into_target,
uint8_t* trampoline_bytes,
intptr_t text_offset)
: callee(callee),
text_offset(text_offset) {}
// The target of the forward call.
RawCode* callee;
// The extra offset into the target.
intptr_t offset_into_target;
// The trampoline buffer.
uint8_t* trampoline_bytes;
// The offset in the .text segment where the trampoline starts.
intptr_t text_offset;
using UnresolvedTrampolineList = IntrusiveDList<UnresolvedTrampoline>;
template <typename ValueType, ValueType kNoValue>
class InstructionsMapTraits {
struct Pair {
RawInstructions* instructions;
ValueType value;
Pair() : instructions(nullptr), value(kNoValue) {}
Pair(RawInstructions* i, const ValueType& value)
: instructions(i), value(value) {}
typedef const RawInstructions* Key;
typedef const ValueType Value;
static Key KeyOf(Pair kv) { return kv.instructions; }
static ValueType ValueOf(Pair kv) { return kv.value; }
static inline intptr_t Hashcode(Key key) {
return reinterpret_cast<intptr_t>(key);
static inline bool IsKeyEqual(Pair pair, Key key) {
return pair.instructions == key;
using InstructionsPosition =
DirectChainedHashMap<InstructionsMapTraits<intptr_t, -1>>;
using TrampolinesMap = DirectChainedHashMap<
InstructionsMapTraits<UnresolvedTrampolineList*, nullptr>>;
using InstructionsUnresolvedCalls = DirectChainedHashMap<
InstructionsMapTraits<SameDestinationUnresolvedCallsList*, nullptr>>;
// Relocates the given code objects by patching the instructions with the
// correct pc offsets.
// Produces a set of [ImageWriterCommand]s which tell the image writer in which
// order (and at which offset) to emit instructions.
class CodeRelocator : public StackResource {
// Relocates instructions of the code objects provided by patching any
// pc-relative calls/jumps.
// Populates the image writer command array which must be used later to write
// the ".text" segment.
static void Relocate(Thread* thread,
GrowableArray<RawCode*>* code_objects,
GrowableArray<ImageWriterCommand>* commands,
bool is_vm_isolate) {
CodeRelocator relocator(thread, code_objects, commands);
CodeRelocator(Thread* thread,
GrowableArray<RawCode*>* code_objects,
GrowableArray<ImageWriterCommand>* commands);
void Relocate(bool is_vm_isolate);
void FindInstructionAndCallLimits();
bool AddInstructionsToText(RawCode* code);
void ScanCallTargets(const Code& code,
const Array& call_targets,
intptr_t code_text_offset);
UnresolvedTrampoline* FindTrampolineFor(UnresolvedCall* unresolved_call);
void AddTrampolineToText(RawInstructions* destination,
uint8_t* trampoline_bytes,
intptr_t trampoline_length);
void EnqueueUnresolvedCall(UnresolvedCall* unresolved_call);
void EnqueueUnresolvedTrampoline(UnresolvedTrampoline* unresolved_trampoline);
bool TryResolveBackwardsCall(UnresolvedCall* unresolved_call);
void ResolveUnresolvedCallsTargeting(const RawInstructions* instructions);
void ResolveCall(UnresolvedCall* unresolved_call);
void ResolveCallToDestination(UnresolvedCall* unresolved_call,
intptr_t destination_text);
void ResolveTrampoline(UnresolvedTrampoline* unresolved_trampoline);
void BuildTrampolinesForAlmostOutOfRangeCalls();
intptr_t FindDestinationInText(const RawInstructions* destination,
intptr_t offset_into_target);
bool IsTargetInRangeFor(UnresolvedCall* unresolved_call,
intptr_t target_text_offset);
// The code relocation happens during AOT snapshot writing and operates on raw
// objects. No allocations can be done.
NoSafepointScope no_savepoint_scope_;
const GrowableArray<RawCode*>* code_objects_;
GrowableArray<ImageWriterCommand>* commands_;
// The size of largest instructions object in bytes.
intptr_t max_instructions_size_ = 0;
// The maximum number of pc-relative calls in an instructions object.
intptr_t max_calls_ = 0;
intptr_t max_offset_into_target_ = 0;
// Data structures used for relocation.
intptr_t next_text_offset_ = 0;
InstructionsPosition text_offsets_;
TrampolinesMap trampolines_by_destination_;
InstructionsUnresolvedCalls unresolved_calls_by_destination_;
AllUnresolvedCallsList all_unresolved_calls_;
// Reusable handles for [ScanCallTargets].
Smi& kind_type_and_offset_;
Object& target_;
Code& destination_;
#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_DBC) && \
// !defined(TARGET_ARCH_IA32)
} // namespace dart