1 | // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_COMPILER_RELOCATION_H_ |
6 | #define RUNTIME_VM_COMPILER_RELOCATION_H_ |
7 | |
8 | #if defined(DART_PRECOMPILED_RUNTIME) |
9 | #error "AOT runtime should not use compiler sources (including header files)" |
10 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
11 | |
12 | #include "vm/allocation.h" |
13 | #include "vm/image_snapshot.h" |
14 | #include "vm/intrusive_dlist.h" |
15 | #include "vm/object.h" |
16 | #include "vm/type_testing_stubs.h" |
17 | #include "vm/visitor.h" |
18 | |
19 | namespace dart { |
20 | |
21 | #if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32) |
22 | |
23 | // Represents a pc-relative call which has not been patched up with the final |
24 | // destination. |
25 | class UnresolvedCall : public IntrusiveDListEntry<UnresolvedCall>, |
26 | public IntrusiveDListEntry<UnresolvedCall, 2> { |
27 | public: |
28 | UnresolvedCall(CodePtr caller, |
29 | intptr_t call_offset, |
30 | intptr_t text_offset, |
31 | CodePtr callee, |
32 | intptr_t offset_into_target, |
33 | bool is_tail_call) |
34 | : caller(caller), |
35 | call_offset(call_offset), |
36 | text_offset(text_offset), |
37 | callee(callee), |
38 | offset_into_target(offset_into_target), |
39 | is_tail_call(is_tail_call) {} |
40 | |
41 | UnresolvedCall(const UnresolvedCall& other) |
42 | : IntrusiveDListEntry<UnresolvedCall>(), |
43 | IntrusiveDListEntry<UnresolvedCall, 2>(), |
44 | caller(other.caller), |
45 | call_offset(other.call_offset), |
46 | text_offset(other.text_offset), |
47 | callee(other.callee), |
48 | offset_into_target(other.offset_into_target), |
49 | is_tail_call(other.is_tail_call) {} |
50 | |
51 | // The caller which has an unresolved call (will be null'ed out when |
52 | // resolved). |
53 | CodePtr caller; |
54 | // The offset from the payload of the calling code which performs the call. |
55 | const intptr_t call_offset; |
56 | // The offset in the .text segment where the call happens. |
57 | const intptr_t text_offset; |
58 | // The target of the forward call (will be null'ed out when resolved). |
59 | CodePtr callee; |
60 | // The extra offset into the target. |
61 | const intptr_t offset_into_target; |
62 | // Whether this is a tail call. |
63 | const bool is_tail_call; |
64 | }; |
65 | |
66 | // A list of all unresolved calls. |
67 | using AllUnresolvedCallsList = IntrusiveDList<UnresolvedCall>; |
68 | |
69 | // A list of all unresolved calls which call the same destination. |
70 | using SameDestinationUnresolvedCallsList = IntrusiveDList<UnresolvedCall, 2>; |
71 | |
72 | // Represents a trampoline which has not been patched up with the final |
73 | // destination. |
74 | // |
75 | // The [CodeRelocator] will insert trampolines into the ".text" segment which |
76 | // increase the range of PC-relative calls. If a pc-relative call in normal |
77 | // code is too far away from it's destination, it will call a trampoline |
78 | // instead (which will tail-call the destination). |
79 | class UnresolvedTrampoline : public IntrusiveDListEntry<UnresolvedTrampoline> { |
80 | public: |
81 | UnresolvedTrampoline(CodePtr callee, |
82 | intptr_t offset_into_target, |
83 | uint8_t* trampoline_bytes, |
84 | intptr_t text_offset) |
85 | : callee(callee), |
86 | offset_into_target(offset_into_target), |
87 | trampoline_bytes(trampoline_bytes), |
88 | text_offset(text_offset) {} |
89 | |
90 | // The target of the forward call. |
91 | CodePtr callee; |
92 | // The extra offset into the target. |
93 | intptr_t offset_into_target; |
94 | |
95 | // The trampoline buffer. |
96 | uint8_t* trampoline_bytes; |
97 | // The offset in the .text segment where the trampoline starts. |
98 | intptr_t text_offset; |
99 | }; |
100 | |
101 | using UnresolvedTrampolineList = IntrusiveDList<UnresolvedTrampoline>; |
102 | |
103 | template <typename ValueType, ValueType kNoValue> |
104 | class InstructionsMapTraits { |
105 | public: |
106 | struct Pair { |
107 | InstructionsPtr instructions; |
108 | ValueType value; |
109 | |
110 | Pair() : instructions(nullptr), value(kNoValue) {} |
111 | Pair(InstructionsPtr i, const ValueType& value) |
112 | : instructions(i), value(value) {} |
113 | }; |
114 | |
115 | typedef const InstructionsPtr Key; |
116 | typedef const ValueType Value; |
117 | |
118 | static Key KeyOf(Pair kv) { return kv.instructions; } |
119 | static ValueType ValueOf(Pair kv) { return kv.value; } |
120 | static inline intptr_t Hashcode(Key key) { |
121 | return static_cast<intptr_t>(key); |
122 | } |
123 | static inline bool IsKeyEqual(Pair pair, Key key) { |
124 | return pair.instructions == key; |
125 | } |
126 | }; |
127 | |
128 | using InstructionsPosition = |
129 | DirectChainedHashMap<InstructionsMapTraits<intptr_t, -1>>; |
130 | |
131 | using TrampolinesMap = DirectChainedHashMap< |
132 | InstructionsMapTraits<UnresolvedTrampolineList*, nullptr>>; |
133 | |
134 | using InstructionsUnresolvedCalls = DirectChainedHashMap< |
135 | InstructionsMapTraits<SameDestinationUnresolvedCallsList*, nullptr>>; |
136 | |
137 | // Relocates the given code objects by patching the instructions with the |
138 | // correct pc offsets. |
139 | // |
140 | // Produces a set of [ImageWriterCommand]s which tell the image writer in which |
141 | // order (and at which offset) to emit instructions. |
142 | class CodeRelocator : public StackResource { |
143 | public: |
144 | // Relocates instructions of the code objects provided by patching any |
145 | // pc-relative calls/jumps. |
146 | // |
147 | // Populates the image writer command array which must be used later to write |
148 | // the ".text" segment. |
149 | static void Relocate(Thread* thread, |
150 | GrowableArray<CodePtr>* code_objects, |
151 | GrowableArray<ImageWriterCommand>* commands, |
152 | bool is_vm_isolate) { |
153 | CodeRelocator relocator(thread, code_objects, commands); |
154 | relocator.Relocate(is_vm_isolate); |
155 | } |
156 | |
157 | private: |
158 | CodeRelocator(Thread* thread, |
159 | GrowableArray<CodePtr>* code_objects, |
160 | GrowableArray<ImageWriterCommand>* commands); |
161 | |
162 | void Relocate(bool is_vm_isolate); |
163 | |
164 | void FindInstructionAndCallLimits(); |
165 | |
166 | bool AddInstructionsToText(CodePtr code); |
167 | void ScanCallTargets(const Code& code, |
168 | const Array& call_targets, |
169 | intptr_t code_text_offset); |
170 | |
171 | UnresolvedTrampoline* FindTrampolineFor(UnresolvedCall* unresolved_call); |
172 | void AddTrampolineToText(InstructionsPtr destination, |
173 | uint8_t* trampoline_bytes, |
174 | intptr_t trampoline_length); |
175 | |
176 | void EnqueueUnresolvedCall(UnresolvedCall* unresolved_call); |
177 | void EnqueueUnresolvedTrampoline(UnresolvedTrampoline* unresolved_trampoline); |
178 | |
179 | bool TryResolveBackwardsCall(UnresolvedCall* unresolved_call); |
180 | void ResolveUnresolvedCallsTargeting(const InstructionsPtr instructions); |
181 | void ResolveCall(UnresolvedCall* unresolved_call); |
182 | void ResolveCallToDestination(UnresolvedCall* unresolved_call, |
183 | intptr_t destination_text); |
184 | void ResolveTrampoline(UnresolvedTrampoline* unresolved_trampoline); |
185 | |
186 | void BuildTrampolinesForAlmostOutOfRangeCalls(); |
187 | |
188 | intptr_t FindDestinationInText(const InstructionsPtr destination, |
189 | intptr_t offset_into_target); |
190 | |
191 | static intptr_t AdjustPayloadOffset(intptr_t payload_offset); |
192 | |
193 | bool IsTargetInRangeFor(UnresolvedCall* unresolved_call, |
194 | intptr_t target_text_offset); |
195 | |
196 | CodePtr GetTarget(const StaticCallsTableEntry& entry); |
197 | |
198 | // The code relocation happens during AOT snapshot writing and operates on raw |
199 | // objects. No allocations can be done. |
200 | NoSafepointScope no_savepoint_scope_; |
201 | Thread* thread_; |
202 | |
203 | const GrowableArray<CodePtr>* code_objects_; |
204 | GrowableArray<ImageWriterCommand>* commands_; |
205 | |
206 | // The size of largest instructions object in bytes. |
207 | intptr_t max_instructions_size_ = 0; |
208 | // The maximum number of pc-relative calls in an instructions object. |
209 | intptr_t max_calls_ = 0; |
210 | intptr_t max_offset_into_target_ = 0; |
211 | |
212 | // Data structures used for relocation. |
213 | intptr_t next_text_offset_ = 0; |
214 | InstructionsPosition text_offsets_; |
215 | TrampolinesMap trampolines_by_destination_; |
216 | InstructionsUnresolvedCalls unresolved_calls_by_destination_; |
217 | AllUnresolvedCallsList all_unresolved_calls_; |
218 | |
219 | // Reusable handles for [ScanCallTargets]. |
220 | Smi& kind_type_and_offset_; |
221 | Object& target_; |
222 | Code& destination_; |
223 | }; |
224 | |
225 | #endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32) |
226 | |
227 | } // namespace dart |
228 | |
229 | #endif // RUNTIME_VM_COMPILER_RELOCATION_H_ |
230 | |