15#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
19 always_generate_trampolines_for_testing,
21 "Generate always trampolines (for testing purposes).");
24 lower_tail_pc_relative_call_distance,
26 "Lower tail call distance.");
28 upper_tail_pc_relative_call_distance,
30 "Upper tail call distance.");
31DEFINE_FLAG(
int, lower_pc_relative_call_distance, -1,
"Lower call distance.");
32DEFINE_FLAG(
int, upper_pc_relative_call_distance, -1,
"Upper call distance.");
34struct TailCallDistanceLimits {
35 static intptr_t Lower() {
36 if (FLAG_lower_tail_pc_relative_call_distance != -1) {
37 return FLAG_lower_tail_pc_relative_call_distance;
41 static intptr_t Upper() {
42 if (FLAG_upper_tail_pc_relative_call_distance != -1) {
43 return FLAG_upper_tail_pc_relative_call_distance;
49struct CallDistanceLimits {
50 static intptr_t Lower() {
51 if (FLAG_lower_pc_relative_call_distance != -1) {
52 return FLAG_lower_pc_relative_call_distance;
56 static intptr_t Upper() {
57 if (FLAG_upper_pc_relative_call_distance != -1) {
58 return FLAG_upper_pc_relative_call_distance;
64const intptr_t kTrampolineSize =
68CodeRelocator::CodeRelocator(Thread* thread,
69 GrowableArray<CodePtr>* code_objects,
70 GrowableArray<ImageWriterCommand>*
commands)
71 : StackResource(thread),
73 code_objects_(code_objects),
75 kind_type_and_offset_(Smi::Handle(thread->zone())),
76 target_(Object::Handle(thread->zone())),
77 destination_(Code::Handle(thread->zone())) {}
79void CodeRelocator::Relocate(
bool is_vm_isolate) {
80 Zone* zone = Thread::Current()->zone();
81 auto& current_caller = Code::Handle(zone);
82 auto& call_targets = Array::Handle(zone);
84 auto& next_caller = Code::Handle(zone);
85 auto& next_caller_targets = Array::Handle(zone);
88 for (intptr_t
i = 0;
i < code_objects_->length(); ++
i) {
89 current_caller = (*code_objects_)[
i];
91 intptr_t code_text_offset;
92 if (!AddInstructionsToText(current_caller.ptr(), &code_text_offset)) {
96 call_targets = current_caller.static_calls_target_table();
97 ScanCallTargets(current_caller, call_targets, code_text_offset);
100 ResolveUnresolvedCallsTargeting(current_caller.instructions());
104 if (
i < (code_objects_->length() - 1)) {
105 next_caller = (*code_objects_)[
i + 1];
106 next_caller_targets = next_caller.static_calls_target_table();
108 next_caller = Code::null();
109 next_caller_targets = Array::null();
111 BuildTrampolinesForAlmostOutOfRangeCalls(next_caller, next_caller_targets);
117 if (!all_unresolved_calls_.IsEmpty()) {
118 for (
auto call : all_unresolved_calls_) {
119 OS::PrintErr(
"Unresolved call to %s from %s\n",
120 Object::Handle(
call->callee).ToCString(),
121 Object::Handle(
call->caller).ToCString());
128 auto it = trampolines_by_destination_.GetIterator();
130 auto entry = it.Next();
131 if (entry ==
nullptr)
break;
133 UnresolvedTrampolineList* trampoline_list = entry->value;
134 while (!trampoline_list->IsEmpty()) {
135 auto unresolved_trampoline = trampoline_list->RemoveFirst();
136 ResolveTrampoline(unresolved_trampoline);
137 delete unresolved_trampoline;
139 delete trampoline_list;
141 trampolines_by_destination_.Clear();
147bool CodeRelocator::AddInstructionsToText(CodePtr
code,
148 intptr_t* code_text_offset) {
149 InstructionsPtr instructions = Code::InstructionsOf(
code);
153 if (text_offsets_.HasKey(instructions)) {
157 if (Instructions::ShouldBeAligned(instructions) &&
159 const intptr_t padding_size =
163 commands_->Add(ImageWriterCommand(next_text_offset_, padding_size));
164 next_text_offset_ += padding_size;
167 *code_text_offset = next_text_offset_;
168 text_offsets_.Insert({instructions, next_text_offset_});
169 commands_->Add(ImageWriterCommand(next_text_offset_,
code));
170 next_text_offset_ += ImageWriter::SizeInSnapshot(instructions);
175UnresolvedTrampoline* CodeRelocator::FindTrampolineFor(
176 UnresolvedCall* unresolved_call) {
177 auto destination = Code::InstructionsOf(unresolved_call->callee);
178 auto entry = trampolines_by_destination_.Lookup(destination);
179 if (entry !=
nullptr) {
180 UnresolvedTrampolineList* trampolines = entry->value;
181 ASSERT(!trampolines->IsEmpty());
188 auto it = trampolines->End();
191 UnresolvedTrampoline* trampoline = *it;
192 if (!IsTargetInRangeFor(unresolved_call, trampoline->text_offset)) {
195 if (trampoline->offset_into_target ==
196 unresolved_call->offset_into_target) {
200 }
while (it != trampolines->Begin());
205void CodeRelocator::AddTrampolineToText(InstructionsPtr destination,
206 uint8_t* trampoline_bytes,
207 intptr_t trampoline_length) {
208 commands_->Add(ImageWriterCommand(next_text_offset_, trampoline_bytes,
210 next_text_offset_ += trampoline_length;
213void CodeRelocator::ScanCallTargets(
const Code&
code,
214 const Array& call_targets,
215 intptr_t code_text_offset) {
216 if (call_targets.IsNull()) {
220 for (
auto call : calls) {
221 kind_type_and_offset_ =
call.Get<Code::kSCallTableKindAndOffset>();
223 const auto return_pc_offset =
225 const auto call_entry_point =
228 if (kind == Code::kCallViaCode) {
232 destination_ = GetTarget(
call);
236 int32_t offset_into_target = 0;
238 intptr_t call_instruction_offset;
239 if (kind == Code::kPcRelativeCall || kind == Code::kPcRelativeTTSCall) {
240 call_instruction_offset =
241 return_pc_offset - PcRelativeCallPattern::kLengthInBytes;
242 PcRelativeCallPattern
call(
code.PayloadStart() + call_instruction_offset);
244 offset_into_target =
call.distance();
245 is_tail_call =
false;
247 ASSERT(kind == Code::kPcRelativeTailCall);
248 call_instruction_offset =
249 return_pc_offset - PcRelativeTailCallPattern::kLengthInBytes;
250 PcRelativeTailCallPattern
call(
code.PayloadStart() +
251 call_instruction_offset);
253 offset_into_target =
call.distance();
257 const uword destination_payload = destination_.PayloadStart();
258 const uword entry_point = call_entry_point == Code::kUncheckedEntry
259 ? destination_.UncheckedEntryPoint()
260 : destination_.EntryPoint();
262 offset_into_target += (entry_point - destination_payload);
264 const intptr_t text_offset =
265 code_text_offset + AdjustPayloadOffset(call_instruction_offset);
267 UnresolvedCall unresolved_call(
code.ptr(), call_instruction_offset,
268 text_offset, destination_.ptr(),
269 offset_into_target, is_tail_call);
270 if (!TryResolveBackwardsCall(&unresolved_call)) {
271 EnqueueUnresolvedCall(
new UnresolvedCall(unresolved_call));
276void CodeRelocator::EnqueueUnresolvedCall(UnresolvedCall* unresolved_call) {
278 all_unresolved_calls_.Append(unresolved_call);
281 InstructionsPtr destination = Code::InstructionsOf(unresolved_call->callee);
282 if (!unresolved_calls_by_destination_.HasKey(destination)) {
283 unresolved_calls_by_destination_.Insert(
284 {destination,
new SameDestinationUnresolvedCallsList()});
286 unresolved_calls_by_destination_.LookupValue(destination)
287 ->Append(unresolved_call);
290void CodeRelocator::EnqueueUnresolvedTrampoline(
291 UnresolvedTrampoline* unresolved_trampoline) {
292 auto destination = Code::InstructionsOf(unresolved_trampoline->callee);
293 auto entry = trampolines_by_destination_.Lookup(destination);
295 UnresolvedTrampolineList* trampolines =
nullptr;
296 if (entry ==
nullptr) {
297 trampolines =
new UnresolvedTrampolineList();
298 trampolines_by_destination_.Insert({destination, trampolines});
300 trampolines = entry->value;
302 trampolines->Append(unresolved_trampoline);
305bool CodeRelocator::TryResolveBackwardsCall(UnresolvedCall* unresolved_call) {
306 auto callee = Code::InstructionsOf(unresolved_call->callee);
307 auto map_entry = text_offsets_.Lookup(callee);
308 if (map_entry ==
nullptr)
return false;
310 if (IsTargetInRangeFor(unresolved_call, map_entry->value)) {
311 ResolveCall(unresolved_call);
317void CodeRelocator::ResolveUnresolvedCallsTargeting(
318 const InstructionsPtr instructions) {
319 if (unresolved_calls_by_destination_.HasKey(instructions)) {
320 SameDestinationUnresolvedCallsList* calls =
321 unresolved_calls_by_destination_.LookupValue(instructions);
322 auto it = calls->Begin();
323 while (it != calls->End()) {
324 UnresolvedCall* unresolved_call = *it;
326 ASSERT(Code::InstructionsOf(unresolved_call->callee) == instructions);
327 ResolveCall(unresolved_call);
330 calls->Remove(unresolved_call);
331 all_unresolved_calls_.Remove(unresolved_call);
333 delete unresolved_call;
337 bool ok = unresolved_calls_by_destination_.Remove(instructions);
342void CodeRelocator::ResolveCall(UnresolvedCall* unresolved_call) {
343 const intptr_t destination_text =
344 FindDestinationInText(Code::InstructionsOf(unresolved_call->callee),
345 unresolved_call->offset_into_target);
347 ResolveCallToDestination(unresolved_call, destination_text);
350void CodeRelocator::ResolveCallToDestination(UnresolvedCall* unresolved_call,
351 intptr_t destination_text) {
352 const intptr_t call_text_offset = unresolved_call->text_offset;
353 const intptr_t call_offset = unresolved_call->call_offset;
355 const int32_t
distance = destination_text - call_text_offset;
357 auto const caller = unresolved_call->caller;
358 uword addr = Code::PayloadStartOf(caller) + call_offset;
359 if (unresolved_call->is_tail_call) {
360 PcRelativeTailCallPattern
call(
addr);
372 unresolved_call->caller =
nullptr;
373 unresolved_call->callee =
nullptr;
376void CodeRelocator::ResolveTrampoline(
377 UnresolvedTrampoline* unresolved_trampoline) {
378 const intptr_t trampoline_text_offset = unresolved_trampoline->text_offset;
379 const uword trampoline_start =
380 reinterpret_cast<uword>(unresolved_trampoline->trampoline_bytes);
382 auto callee = Code::InstructionsOf(unresolved_trampoline->callee);
383 auto destination_text =
384 FindDestinationInText(callee, unresolved_trampoline->offset_into_target);
385 const int32_t
distance = destination_text - trampoline_text_offset;
387 PcRelativeTrampolineJumpPattern pattern(trampoline_start);
388 pattern.Initialize();
393bool CodeRelocator::IsTargetInRangeFor(UnresolvedCall* unresolved_call,
394 intptr_t target_text_offset) {
395 const auto forward_distance =
396 target_text_offset - unresolved_call->text_offset;
397 if (unresolved_call->is_tail_call) {
398 return TailCallDistanceLimits::Lower() <= forward_distance &&
399 forward_distance <= TailCallDistanceLimits::Upper();
401 return CallDistanceLimits::Lower() <= forward_distance &&
402 forward_distance <= CallDistanceLimits::Upper();
409 ASSERT(
call.Get<Code::kSCallTableFunctionTarget>() == Function::null());
411 target_ =
call.Get<Code::kSCallTableCodeOrTypeTarget>();
412 if (target_.IsAbstractType()) {
413 target_ = AbstractType::Cast(target_).type_test_stub();
414 destination_ = Code::Cast(target_).ptr();
426 if (destination_.InVMIsolateHeap()) {
427 auto object_store = thread_->isolate_group()->object_store();
429 if (destination_.ptr() == StubCode::DefaultTypeTest().ptr()) {
430 destination_ = object_store->default_tts_stub();
431 }
else if (destination_.ptr() ==
432 StubCode::DefaultNullableTypeTest().ptr()) {
433 destination_ = object_store->default_nullable_tts_stub();
434 }
else if (destination_.ptr() == StubCode::TopTypeTypeTest().ptr()) {
435 destination_ = object_store->top_type_tts_stub();
436 }
else if (destination_.ptr() == StubCode::UnreachableTypeTest().ptr()) {
437 destination_ = object_store->unreachable_tts_stub();
438 }
else if (destination_.ptr() == StubCode::SlowTypeTest().ptr()) {
439 destination_ = object_store->slow_tts_stub();
440 }
else if (destination_.ptr() ==
441 StubCode::NullableTypeParameterTypeTest().ptr()) {
442 destination_ = object_store->nullable_type_parameter_tts_stub();
443 }
else if (destination_.ptr() ==
444 StubCode::TypeParameterTypeTest().ptr()) {
445 destination_ = object_store->type_parameter_tts_stub();
452 destination_ = Code::Cast(target_).ptr();
454 ASSERT(!destination_.InVMIsolateHeap());
455 return destination_.ptr();
458void CodeRelocator::BuildTrampolinesForAlmostOutOfRangeCalls(
459 const Code& next_caller,
460 const Array& next_caller_targets) {
461 const bool all_functions_emitted = next_caller.IsNull();
463 bool next_requires_alignment =
false;
465 uword next_call_count = 0;
466 if (!all_functions_emitted) {
467 next_size = ImageWriter::SizeInSnapshot(next_caller.instructions());
468 next_requires_alignment =
469 Instructions::ShouldBeAligned(next_caller.instructions());
470 if (!next_caller_targets.IsNull()) {
472 next_call_count = calls.Length();
476 while (!all_unresolved_calls_.IsEmpty()) {
477 UnresolvedCall* unresolved_call = all_unresolved_calls_.First();
479 if (!all_functions_emitted) {
484 const intptr_t next_start =
485 next_requires_alignment
488 const intptr_t future_boundary =
489 next_start + next_size +
491 (unresolved_calls_by_destination_.Length() + next_call_count - 1);
492 if (IsTargetInRangeFor(unresolved_call, future_boundary) &&
493 !FLAG_always_generate_trampolines_for_testing) {
505 ASSERT(IsTargetInRangeFor(unresolved_call, next_text_offset_));
508 intptr_t trampoline_text_offset = -1;
509 auto callee = Code::InstructionsOf(unresolved_call->callee);
511 if (!FLAG_always_generate_trampolines_for_testing) {
512 auto old_trampoline_entry = FindTrampolineFor(unresolved_call);
513 if (old_trampoline_entry !=
nullptr) {
514 trampoline_text_offset = old_trampoline_entry->text_offset;
519 if (trampoline_text_offset == -1) {
523 auto trampoline_bytes =
new uint8_t[kTrampolineSize];
525 for (uint8_t* cur = trampoline_bytes;
526 cur < trampoline_bytes + kTrampolineSize;
531 auto unresolved_trampoline =
new UnresolvedTrampoline{
532 unresolved_call->callee,
533 unresolved_call->offset_into_target,
537 AddTrampolineToText(callee, trampoline_bytes, kTrampolineSize);
538 EnqueueUnresolvedTrampoline(unresolved_trampoline);
539 trampoline_text_offset = unresolved_trampoline->text_offset;
544 auto destination = Code::InstructionsOf(unresolved_call->callee);
545 ResolveCallToDestination(unresolved_call, trampoline_text_offset);
549 auto calls = unresolved_calls_by_destination_.LookupValue(destination);
550 calls->Remove(unresolved_call);
551 all_unresolved_calls_.Remove(unresolved_call);
552 delete unresolved_call;
555 if (calls->IsEmpty()) {
556 unresolved_calls_by_destination_.Remove(destination);
562intptr_t CodeRelocator::FindDestinationInText(
const InstructionsPtr destination,
563 intptr_t offset_into_target) {
564 auto const destination_offset = text_offsets_.LookupValue(destination);
565 return destination_offset + AdjustPayloadOffset(offset_into_target);
568intptr_t CodeRelocator::AdjustPayloadOffset(intptr_t payload_offset) {
569 if (FLAG_precompiled_mode) {
570 return payload_offset;
static bool ok(int result)
#define RELEASE_ASSERT(cond)
static constexpr int32_t kLowerCallingRange
static constexpr int32_t kUpperCallingRange
static constexpr int32_t kUpperCallingRange
static constexpr int32_t kLowerCallingRange
static constexpr int kLengthInBytes
static constexpr T RoundUp(T x, uintptr_t alignment, uintptr_t offset=0)
static const word kBarePayloadAlignment
static constexpr int HeaderSize
StaticCallsTable::TupleView StaticCallsTableEntry
constexpr uword kBreakInstructionFiller
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
constexpr intptr_t kWordSize
ArrayOfTuplesView< Code::SCallTableEntry, std::tuple< Smi, Object, Function > > StaticCallsTable
const intptr_t kPreferredLoopAlignment
static void RoundUp(Vector< char > buffer, int *length, int *decimal_point)
static DecodeResult decode(std::string path)