3fd6fa4ff7
Change-Id: Ia3127ba9ef4ee1faf1f85a6eaa0dafd0cf187b2b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99376 Reviewed-by: Aart Bik <ajcbik@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Samir Jindel <sjindel@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
217 lines
7.6 KiB
C++
217 lines
7.6 KiB
C++
// 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,
|
|
intptr_t text_offset,
|
|
RawCode* callee,
|
|
intptr_t offset_into_target)
|
|
: caller(caller),
|
|
call_offset(call_offset),
|
|
text_offset(text_offset),
|
|
callee(callee),
|
|
offset_into_target(offset_into_target) {}
|
|
|
|
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) {}
|
|
|
|
// 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> {
|
|
public:
|
|
UnresolvedTrampoline(RawCode* 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.
|
|
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 {
|
|
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 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<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);
|
|
|
|
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
|
|
|
|
#endif // RUNTIME_VM_COMPILER_RELOCATION_H_
|