blob: e9b6d44eb2bb744a642accd6f4784d3d9d7a37d2 [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_
#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#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_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(CodePtr caller,
intptr_t call_offset,
intptr_t text_offset,
CodePtr callee,
intptr_t offset_into_target,
bool is_tail_call)
: caller(caller),
call_offset(call_offset),
text_offset(text_offset),
callee(callee),
offset_into_target(offset_into_target),
is_tail_call(is_tail_call) {}
UnresolvedCall(const UnresolvedCall& other)
: IntrusiveDListEntry<UnresolvedCall>(),
IntrusiveDListEntry<UnresolvedCall, 2>(),
caller(other.caller),
call_offset(other.call_offset),
text_offset(other.text_offset),
callee(other.callee),
offset_into_target(other.offset_into_target),
is_tail_call(other.is_tail_call) {}
// The caller which has an unresolved call (will be null'ed out when
// resolved).
CodePtr caller;
// The offset from the payload of the calling code which performs the call.
const intptr_t call_offset;
// The offset in the .text segment where the call happens.
const intptr_t text_offset;
// The target of the forward call (will be null'ed out when resolved).
CodePtr callee;
// The extra offset into the target.
const intptr_t offset_into_target;
// Whether this is a tail call.
const bool is_tail_call;
};
// 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> {
public:
UnresolvedTrampoline(CodePtr callee,
intptr_t offset_into_target,
uint8_t* trampoline_bytes,
intptr_t text_offset)
: callee(callee),
offset_into_target(offset_into_target),
trampoline_bytes(trampoline_bytes),
text_offset(text_offset) {}
// The target of the forward call.
CodePtr 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 {
public:
struct Pair {
InstructionsPtr instructions;
ValueType value;
Pair() : instructions(nullptr), value(kNoValue) {}
Pair(InstructionsPtr i, const ValueType& value)
: instructions(i), value(value) {}
};
typedef const InstructionsPtr 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 static_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 {
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<CodePtr>* 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<CodePtr>* code_objects,
GrowableArray<ImageWriterCommand>* commands);
void Relocate(bool is_vm_isolate);
void FindInstructionAndCallLimits();
bool AddInstructionsToText(CodePtr code);
void ScanCallTargets(const Code& code,
const Array& call_targets,
intptr_t code_text_offset);
UnresolvedTrampoline* FindTrampolineFor(UnresolvedCall* unresolved_call);
void AddTrampolineToText(InstructionsPtr 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 InstructionsPtr 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 InstructionsPtr destination,
intptr_t offset_into_target);
static intptr_t AdjustPayloadOffset(intptr_t payload_offset);
bool IsTargetInRangeFor(UnresolvedCall* unresolved_call,
intptr_t target_text_offset);
CodePtr GetTarget(const StaticCallsTableEntry& entry);
// The code relocation happens during AOT snapshot writing and operates on raw
// objects. No allocations can be done.
NoSafepointScope no_savepoint_scope_;
Thread* thread_;
const GrowableArray<CodePtr>* 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_IA32)
} // namespace dart
#endif // RUNTIME_VM_COMPILER_RELOCATION_H_