// 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, public IntrusiveDListEntry { 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(), IntrusiveDListEntry(), 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; // A list of all unresolved calls which call the same destination. using SameDestinationUnresolvedCallsList = IntrusiveDList; // 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 { 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; template 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 uword Hash(Key key) { return Utils::WordHash(static_cast(key)); } static inline bool IsKeyEqual(Pair pair, Key key) { return pair.instructions == key; } }; using InstructionsPosition = DirectChainedHashMap>; using TrampolinesMap = DirectChainedHashMap< InstructionsMapTraits>; using InstructionsUnresolvedCalls = DirectChainedHashMap< InstructionsMapTraits>; // 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* code_objects, GrowableArray* commands, bool is_vm_isolate) { CodeRelocator relocator(thread, code_objects, commands); relocator.Relocate(is_vm_isolate); } private: CodeRelocator(Thread* thread, GrowableArray* code_objects, GrowableArray* commands); void Relocate(bool is_vm_isolate); void FindLargestInstruction(); 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(bool force); 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* code_objects_; GrowableArray* 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_; 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_