blob: 391743275c2a2e70817c566f4c599cbe67230768 [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.
#ifndef RUNTIME_VM_COMPILER_RELOCATION_H_
#define RUNTIME_VM_COMPILER_RELOCATION_H_
#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) && \
!defined(TARGET_ARCH_IA32)
// Represents a pc-relative call which has not been patched up with the final
// destination.
class UnresolvedCall : public IntrusiveDListEntry<UnresolvedCall>,
public IntrusiveDListEntry<UnresolvedCall, 2> {
public:
UnresolvedCall(RawCode* caller,
intptr_t call_offset,
Code::CallEntryPoint call_entry_point,
intptr_t text_offset,
RawCode* callee)
: caller(caller),
call_offset(call_offset),
call_entry_point(call_entry_point),
text_offset(text_offset),
callee(callee) {}
UnresolvedCall(const UnresolvedCall& other)
: caller(other.caller),
call_offset(other.call_offset),
call_entry_point(other.call_entry_point),
text_offset(other.text_offset),
callee(other.callee) {}
// 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;
// Type entry-point type we call in the destination.
Code::CallEntryPoint call_entry_point;
// The offset in the .text segment where the call happens.
intptr_t text_offset;
// The target of the forward call.
RawCode* callee;
};
// 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).
struct UnresolvedTrampoline {
// The entry-point type we call in the destination.
Code::CallEntryPoint call_entry_point;
// The target of the forward call.
RawCode* callee;
// The trampoline buffer.
uint8_t* trampoline_bytes;
// The offset in the .text segment where the trampoline starts.
intptr_t text_offset;
};
template <typename ValueType, ValueType kNoValue>
class InstructionsMapTraits {
public:
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 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 {
public:
// 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);
relocator.Relocate(is_vm_isolate);
}
private:
CodeRelocator(Thread* thread,
GrowableArray<RawCode*>* code_objects,
GrowableArray<ImageWriterCommand>* commands);
void Relocate(bool is_vm_isolate);
bool AddInstructionsToText(RawCode* code);
void AddTrampolineToText(RawInstructions* destination,
uint8_t* trampoline_bytes,
intptr_t trampoline_length);
void ScanCallTargets(const Code& code,
const Array& call_targets,
intptr_t code_text_offset);
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,
Code::CallEntryPoint call_entry_point);
bool IsTargetInRangeFor(UnresolvedCall* unresolved_call,
intptr_t target_text_offset);
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;
// Data structures used for relocation.
intptr_t next_text_offset_ = 0;
InstructionsPosition text_offsets_;
InstructionsPosition trampoline_text_offsets_;
InstructionsUnresolvedCalls unresolved_calls_by_destination_;
AllUnresolvedCallsList all_unresolved_calls_;
GrowableArray<UnresolvedTrampoline*> unresolved_trampolines_;
// 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
#endif // RUNTIME_VM_COMPILER_RELOCATION_H_