1// Copyright (c) 2013, 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#include "vm/compiler/backend/flow_graph_compiler.h"
6#include "vm/globals.h" // Needed here to get TARGET_ARCH_XXX.
7
8#include "platform/utils.h"
9#include "vm/bit_vector.h"
10#include "vm/compiler/backend/code_statistics.h"
11#include "vm/compiler/backend/il_printer.h"
12#include "vm/compiler/backend/inliner.h"
13#include "vm/compiler/backend/linearscan.h"
14#include "vm/compiler/backend/locations.h"
15#include "vm/compiler/backend/loops.h"
16#include "vm/compiler/cha.h"
17#include "vm/compiler/intrinsifier.h"
18#include "vm/compiler/jit/compiler.h"
19#include "vm/dart_entry.h"
20#include "vm/debugger.h"
21#include "vm/deopt_instructions.h"
22#include "vm/exceptions.h"
23#include "vm/flags.h"
24#include "vm/kernel_isolate.h"
25#include "vm/log.h"
26#include "vm/longjump.h"
27#include "vm/object_store.h"
28#include "vm/parser.h"
29#include "vm/raw_object.h"
30#include "vm/resolver.h"
31#include "vm/service_isolate.h"
32#include "vm/stack_frame.h"
33#include "vm/stub_code.h"
34#include "vm/symbols.h"
35#include "vm/timeline.h"
36#include "vm/type_testing_stubs.h"
37
38namespace dart {
39
40DEFINE_FLAG(bool,
41 trace_inlining_intervals,
42 false,
43 "Inlining interval diagnostics");
44
45DEFINE_FLAG(bool, enable_peephole, true, "Enable peephole optimization");
46
47DEFINE_FLAG(bool,
48 enable_simd_inline,
49 true,
50 "Enable inlining of SIMD related method calls.");
51DEFINE_FLAG(int,
52 min_optimization_counter_threshold,
53 5000,
54 "The minimum invocation count for a function.");
55DEFINE_FLAG(int,
56 optimization_counter_scale,
57 2000,
58 "The scale of invocation count, by size of the function.");
59DEFINE_FLAG(bool, source_lines, false, "Emit source line as assembly comment.");
60
61DECLARE_FLAG(charp, deoptimize_filter);
62DECLARE_FLAG(bool, intrinsify);
63DECLARE_FLAG(int, regexp_optimization_counter_threshold);
64DECLARE_FLAG(int, reoptimization_counter_threshold);
65DECLARE_FLAG(int, stacktrace_every);
66DECLARE_FLAG(charp, stacktrace_filter);
67DECLARE_FLAG(int, gc_every);
68DECLARE_FLAG(bool, trace_compiler);
69
70// Assign locations to incoming arguments, i.e., values pushed above spill slots
71// with PushArgument. Recursively allocates from outermost to innermost
72// environment.
73void CompilerDeoptInfo::AllocateIncomingParametersRecursive(
74 Environment* env,
75 intptr_t* stack_height) {
76 if (env == NULL) return;
77 AllocateIncomingParametersRecursive(env->outer(), stack_height);
78 for (Environment::ShallowIterator it(env); !it.Done(); it.Advance()) {
79 if (it.CurrentLocation().IsInvalid() &&
80 it.CurrentValue()->definition()->IsPushArgument()) {
81 it.SetCurrentLocation(Location::StackSlot(
82 compiler::target::frame_layout.FrameSlotForVariableIndex(
83 -*stack_height),
84 FPREG));
85 (*stack_height)++;
86 }
87 }
88}
89
90void CompilerDeoptInfo::EmitMaterializations(Environment* env,
91 DeoptInfoBuilder* builder) {
92 for (Environment::DeepIterator it(env); !it.Done(); it.Advance()) {
93 if (it.CurrentLocation().IsInvalid()) {
94 MaterializeObjectInstr* mat =
95 it.CurrentValue()->definition()->AsMaterializeObject();
96 ASSERT(mat != NULL);
97 builder->AddMaterialization(mat);
98 }
99 }
100}
101
102FlowGraphCompiler::FlowGraphCompiler(
103 compiler::Assembler* assembler,
104 FlowGraph* flow_graph,
105 const ParsedFunction& parsed_function,
106 bool is_optimizing,
107 SpeculativeInliningPolicy* speculative_policy,
108 const GrowableArray<const Function*>& inline_id_to_function,
109 const GrowableArray<TokenPosition>& inline_id_to_token_pos,
110 const GrowableArray<intptr_t>& caller_inline_id,
111 ZoneGrowableArray<const ICData*>* deopt_id_to_ic_data,
112 CodeStatistics* stats /* = NULL */)
113 : thread_(Thread::Current()),
114 zone_(Thread::Current()->zone()),
115 assembler_(assembler),
116 parsed_function_(parsed_function),
117 flow_graph_(*flow_graph),
118 block_order_(*flow_graph->CodegenBlockOrder(is_optimizing)),
119 current_block_(nullptr),
120 exception_handlers_list_(nullptr),
121 pc_descriptors_list_(nullptr),
122 compressed_stackmaps_builder_(nullptr),
123 code_source_map_builder_(nullptr),
124 catch_entry_moves_maps_builder_(nullptr),
125 block_info_(block_order_.length()),
126 deopt_infos_(),
127 static_calls_target_table_(),
128 indirect_gotos_(),
129 is_optimizing_(is_optimizing),
130 speculative_policy_(speculative_policy),
131 may_reoptimize_(false),
132 intrinsic_mode_(false),
133 stats_(stats),
134 double_class_(
135 Class::ZoneHandle(isolate()->object_store()->double_class())),
136 mint_class_(Class::ZoneHandle(isolate()->object_store()->mint_class())),
137 float32x4_class_(
138 Class::ZoneHandle(isolate()->object_store()->float32x4_class())),
139 float64x2_class_(
140 Class::ZoneHandle(isolate()->object_store()->float64x2_class())),
141 int32x4_class_(
142 Class::ZoneHandle(isolate()->object_store()->int32x4_class())),
143 list_class_(Class::ZoneHandle(Library::Handle(Library::CoreLibrary())
144 .LookupClass(Symbols::List()))),
145 parallel_move_resolver_(this),
146 pending_deoptimization_env_(NULL),
147 deopt_id_to_ic_data_(deopt_id_to_ic_data),
148 edge_counters_array_(Array::ZoneHandle()) {
149 ASSERT(flow_graph->parsed_function().function().raw() ==
150 parsed_function.function().raw());
151 if (is_optimizing) {
152 // No need to collect extra ICData objects created during compilation.
153 deopt_id_to_ic_data_ = nullptr;
154 } else {
155 const intptr_t len = thread()->compiler_state().deopt_id();
156 deopt_id_to_ic_data_->EnsureLength(len, nullptr);
157 }
158 ASSERT(assembler != NULL);
159 ASSERT(!list_class_.IsNull());
160
161#if defined(PRODUCT)
162 const bool stack_traces_only = true;
163#else
164 const bool stack_traces_only = false;
165#endif
166 code_source_map_builder_ = new (zone_)
167 CodeSourceMapBuilder(stack_traces_only, caller_inline_id,
168 inline_id_to_token_pos, inline_id_to_function);
169
170 ArchSpecificInitialization();
171}
172
173bool FlowGraphCompiler::IsUnboxedField(const Field& field) {
174 // The `field.is_non_nullable_integer()` is set in the kernel loader and can
175 // only be set if we consume a AOT kernel (annotated with inferred types).
176 ASSERT(!field.is_non_nullable_integer() || FLAG_precompiled_mode);
177 const bool valid_class =
178 (SupportsUnboxedDoubles() && (field.guarded_cid() == kDoubleCid)) ||
179 (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat32x4Cid)) ||
180 (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat64x2Cid)) ||
181 field.is_non_nullable_integer();
182 return field.is_unboxing_candidate() && !field.is_nullable() && valid_class;
183}
184
185bool FlowGraphCompiler::IsPotentialUnboxedField(const Field& field) {
186 if (FLAG_precompiled_mode) {
187 // kernel_loader.cc:ReadInferredType sets the guarded cid for fields based
188 // on inferred types from TFA (if available). The guarded cid is therefore
189 // proven to be correct.
190 return IsUnboxedField(field);
191 }
192 return field.is_unboxing_candidate() &&
193 (FlowGraphCompiler::IsUnboxedField(field) ||
194 (field.guarded_cid() == kIllegalCid));
195}
196
197void FlowGraphCompiler::InitCompiler() {
198 pc_descriptors_list_ = new (zone()) DescriptorList(64);
199 exception_handlers_list_ = new (zone()) ExceptionHandlerList();
200#if defined(DART_PRECOMPILER)
201 catch_entry_moves_maps_builder_ = new (zone()) CatchEntryMovesMapBuilder();
202#endif
203 block_info_.Clear();
204 // Initialize block info and search optimized (non-OSR) code for calls
205 // indicating a non-leaf routine and calls without IC data indicating
206 // possible reoptimization.
207
208 for (int i = 0; i < block_order_.length(); ++i) {
209 block_info_.Add(new (zone()) BlockInfo());
210 if (is_optimizing() && !flow_graph().IsCompiledForOsr()) {
211 BlockEntryInstr* entry = block_order_[i];
212 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
213 Instruction* current = it.Current();
214 if (current->IsBranch()) {
215 current = current->AsBranch()->comparison();
216 }
217 // In optimized code, ICData is always set in the instructions.
218 const ICData* ic_data = NULL;
219 if (current->IsInstanceCall()) {
220 ic_data = current->AsInstanceCall()->ic_data();
221 }
222 if ((ic_data != NULL) && (ic_data->NumberOfUsedChecks() == 0)) {
223 may_reoptimize_ = true;
224 }
225 }
226 }
227 }
228
229 if (!is_optimizing() && FLAG_reorder_basic_blocks) {
230 // Initialize edge counter array.
231 const intptr_t num_counters = flow_graph_.preorder().length();
232 const Array& edge_counters =
233 Array::Handle(Array::New(num_counters, Heap::kOld));
234 for (intptr_t i = 0; i < num_counters; ++i) {
235 edge_counters.SetAt(i, Object::smi_zero());
236 }
237 edge_counters_array_ = edge_counters.raw();
238 }
239}
240
241bool FlowGraphCompiler::CanOptimize() {
242 return FLAG_optimization_counter_threshold >= 0;
243}
244
245bool FlowGraphCompiler::CanOptimizeFunction() const {
246 return CanOptimize() && !parsed_function().function().HasBreakpoint();
247}
248
249bool FlowGraphCompiler::CanOSRFunction() const {
250 return isolate()->use_osr() && CanOptimizeFunction() && !is_optimizing();
251}
252
253void FlowGraphCompiler::InsertBSSRelocation(BSS::Relocation reloc) {
254 const intptr_t offset = assembler()->InsertAlignedRelocation(reloc);
255 AddDescriptor(PcDescriptorsLayout::kBSSRelocation, /*pc_offset=*/offset,
256 /*deopt_id=*/DeoptId::kNone, TokenPosition::kNoSource,
257 /*try_index=*/-1);
258}
259
260bool FlowGraphCompiler::ForceSlowPathForStackOverflow() const {
261#if !defined(PRODUCT)
262 if ((FLAG_stacktrace_every > 0) || (FLAG_deoptimize_every > 0) ||
263 (FLAG_gc_every > 0) ||
264 (isolate()->reload_every_n_stack_overflow_checks() > 0)) {
265 if (!Isolate::IsVMInternalIsolate(isolate())) {
266 return true;
267 }
268 }
269 if (FLAG_stacktrace_filter != NULL &&
270 strstr(parsed_function().function().ToFullyQualifiedCString(),
271 FLAG_stacktrace_filter) != NULL) {
272 return true;
273 }
274 if (is_optimizing() && FLAG_deoptimize_filter != NULL &&
275 strstr(parsed_function().function().ToFullyQualifiedCString(),
276 FLAG_deoptimize_filter) != NULL) {
277 return true;
278 }
279#endif // !defined(PRODUCT)
280 return false;
281}
282
283bool FlowGraphCompiler::IsEmptyBlock(BlockEntryInstr* block) const {
284 // Entry-points cannot be merged because they must have assembly
285 // prologue emitted which should not be included in any block they jump to.
286 return !block->IsGraphEntry() && !block->IsFunctionEntry() &&
287 !block->IsCatchBlockEntry() && !block->IsOsrEntry() &&
288 !block->IsIndirectEntry() && !block->HasNonRedundantParallelMove() &&
289 block->next()->IsGoto() &&
290 !block->next()->AsGoto()->HasNonRedundantParallelMove();
291}
292
293void FlowGraphCompiler::CompactBlock(BlockEntryInstr* block) {
294 BlockInfo* block_info = block_info_[block->postorder_number()];
295
296 // Break out of cycles in the control flow graph.
297 if (block_info->is_marked()) {
298 return;
299 }
300 block_info->mark();
301
302 if (IsEmptyBlock(block)) {
303 // For empty blocks, record a corresponding nonempty target as their
304 // jump label.
305 BlockEntryInstr* target = block->next()->AsGoto()->successor();
306 CompactBlock(target);
307 block_info->set_jump_label(GetJumpLabel(target));
308 }
309}
310
311void FlowGraphCompiler::CompactBlocks() {
312 // This algorithm does not garbage collect blocks in place, but merely
313 // records forwarding label information. In this way it avoids having to
314 // change join and target entries.
315 compiler::Label* nonempty_label = NULL;
316 for (intptr_t i = block_order().length() - 1; i >= 1; --i) {
317 BlockEntryInstr* block = block_order()[i];
318
319 // Unoptimized code must emit all possible deoptimization points.
320 if (is_optimizing()) {
321 CompactBlock(block);
322 }
323
324 // For nonempty blocks, record the next nonempty block in the block
325 // order. Since no code is emitted for empty blocks, control flow is
326 // eligible to fall through to the next nonempty one.
327 if (!WasCompacted(block)) {
328 BlockInfo* block_info = block_info_[block->postorder_number()];
329 block_info->set_next_nonempty_label(nonempty_label);
330 nonempty_label = GetJumpLabel(block);
331 }
332 }
333
334 ASSERT(block_order()[0]->IsGraphEntry());
335 BlockInfo* block_info = block_info_[block_order()[0]->postorder_number()];
336 block_info->set_next_nonempty_label(nonempty_label);
337}
338
339#if defined(DART_PRECOMPILER)
340static intptr_t LocationToStackIndex(const Location& src) {
341 ASSERT(src.HasStackIndex());
342 return -compiler::target::frame_layout.VariableIndexForFrameSlot(
343 src.stack_index());
344}
345
346static CatchEntryMove CatchEntryMoveFor(compiler::Assembler* assembler,
347 Representation src_type,
348 const Location& src,
349 intptr_t dst_index) {
350 if (src.IsConstant()) {
351 // Skip dead locations.
352 if (src.constant().raw() == Symbols::OptimizedOut().raw()) {
353 return CatchEntryMove();
354 }
355 const intptr_t pool_index =
356 assembler->object_pool_builder().FindObject(src.constant());
357 return CatchEntryMove::FromSlot(CatchEntryMove::SourceKind::kConstant,
358 pool_index, dst_index);
359 }
360
361 if (src.IsPairLocation()) {
362 const auto lo_loc = src.AsPairLocation()->At(0);
363 const auto hi_loc = src.AsPairLocation()->At(1);
364 ASSERT(lo_loc.IsStackSlot() && hi_loc.IsStackSlot());
365 return CatchEntryMove::FromSlot(
366 CatchEntryMove::SourceKind::kInt64PairSlot,
367 CatchEntryMove::EncodePairSource(LocationToStackIndex(lo_loc),
368 LocationToStackIndex(hi_loc)),
369 dst_index);
370 }
371
372 CatchEntryMove::SourceKind src_kind;
373 switch (src_type) {
374 case kTagged:
375 src_kind = CatchEntryMove::SourceKind::kTaggedSlot;
376 break;
377 case kUnboxedInt64:
378 src_kind = CatchEntryMove::SourceKind::kInt64Slot;
379 break;
380 case kUnboxedInt32:
381 src_kind = CatchEntryMove::SourceKind::kInt32Slot;
382 break;
383 case kUnboxedUint32:
384 src_kind = CatchEntryMove::SourceKind::kUint32Slot;
385 break;
386 case kUnboxedDouble:
387 src_kind = CatchEntryMove::SourceKind::kDoubleSlot;
388 break;
389 case kUnboxedFloat32x4:
390 src_kind = CatchEntryMove::SourceKind::kFloat32x4Slot;
391 break;
392 case kUnboxedFloat64x2:
393 src_kind = CatchEntryMove::SourceKind::kFloat64x2Slot;
394 break;
395 case kUnboxedInt32x4:
396 src_kind = CatchEntryMove::SourceKind::kInt32x4Slot;
397 break;
398 default:
399 UNREACHABLE();
400 break;
401 }
402
403 return CatchEntryMove::FromSlot(src_kind, LocationToStackIndex(src),
404 dst_index);
405}
406#endif
407
408void FlowGraphCompiler::RecordCatchEntryMoves(Environment* env,
409 intptr_t try_index) {
410#if defined(DART_PRECOMPILER)
411 env = env ? env : pending_deoptimization_env_;
412 try_index = try_index != kInvalidTryIndex ? try_index : CurrentTryIndex();
413 if (is_optimizing() && env != nullptr && (try_index != kInvalidTryIndex)) {
414 env = env->Outermost();
415 CatchBlockEntryInstr* catch_block =
416 flow_graph().graph_entry()->GetCatchEntry(try_index);
417 const GrowableArray<Definition*>* idefs =
418 catch_block->initial_definitions();
419 catch_entry_moves_maps_builder_->NewMapping(assembler()->CodeSize());
420
421 const intptr_t num_direct_parameters = flow_graph().num_direct_parameters();
422 const intptr_t ex_idx =
423 catch_block->raw_exception_var() != nullptr
424 ? flow_graph().EnvIndex(catch_block->raw_exception_var())
425 : -1;
426 const intptr_t st_idx =
427 catch_block->raw_stacktrace_var() != nullptr
428 ? flow_graph().EnvIndex(catch_block->raw_stacktrace_var())
429 : -1;
430 for (intptr_t i = 0; i < flow_graph().variable_count(); ++i) {
431 // Don't sync captured parameters. They are not in the environment.
432 if (flow_graph().captured_parameters()->Contains(i)) continue;
433 // Don't sync exception or stack trace variables.
434 if (i == ex_idx || i == st_idx) continue;
435 // Don't sync values that have been replaced with constants.
436 if ((*idefs)[i]->IsConstant()) continue;
437
438 Location src = env->LocationAt(i);
439 // Can only occur if AllocationSinking is enabled - and it is disabled
440 // in functions with try.
441 ASSERT(!src.IsInvalid());
442 const Representation src_type =
443 env->ValueAt(i)->definition()->representation();
444 intptr_t dest_index = i - num_direct_parameters;
445 const auto move =
446 CatchEntryMoveFor(assembler(), src_type, src, dest_index);
447 if (!move.IsRedundant()) {
448 catch_entry_moves_maps_builder_->Append(move);
449 }
450 }
451
452 catch_entry_moves_maps_builder_->EndMapping();
453 }
454#endif // defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
455}
456
457void FlowGraphCompiler::EmitCallsiteMetadata(TokenPosition token_pos,
458 intptr_t deopt_id,
459 PcDescriptorsLayout::Kind kind,
460 LocationSummary* locs,
461 Environment* env) {
462 AddCurrentDescriptor(kind, deopt_id, token_pos);
463 RecordSafepoint(locs);
464 RecordCatchEntryMoves(env);
465 if ((deopt_id != DeoptId::kNone) && !FLAG_precompiled_mode) {
466 // Marks either the continuation point in unoptimized code or the
467 // deoptimization point in optimized code, after call.
468 const intptr_t deopt_id_after = DeoptId::ToDeoptAfter(deopt_id);
469 if (is_optimizing()) {
470 AddDeoptIndexAtCall(deopt_id_after);
471 } else {
472 // Add deoptimization continuation point after the call and before the
473 // arguments are removed.
474 AddCurrentDescriptor(PcDescriptorsLayout::kDeopt, deopt_id_after,
475 token_pos);
476 }
477 }
478}
479
480void FlowGraphCompiler::EmitYieldPositionMetadata(TokenPosition token_pos,
481 intptr_t yield_index) {
482 AddDescriptor(PcDescriptorsLayout::kOther, assembler()->CodeSize(),
483 DeoptId::kNone, token_pos, CurrentTryIndex(), yield_index);
484}
485
486void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) {
487 if (!is_optimizing()) {
488 if (instr->CanBecomeDeoptimizationTarget() && !instr->IsGoto()) {
489 // Instructions that can be deoptimization targets need to record kDeopt
490 // PcDescriptor corresponding to their deopt id. GotoInstr records its
491 // own so that it can control the placement.
492 AddCurrentDescriptor(PcDescriptorsLayout::kDeopt, instr->deopt_id(),
493 instr->token_pos());
494 }
495 AllocateRegistersLocally(instr);
496 }
497}
498
499void FlowGraphCompiler::EmitSourceLine(Instruction* instr) {
500 if (!instr->token_pos().IsReal() || (instr->env() == NULL)) {
501 return;
502 }
503 const Script& script =
504 Script::Handle(zone(), instr->env()->function().script());
505 intptr_t line_nr;
506 intptr_t column_nr;
507 script.GetTokenLocation(instr->token_pos(), &line_nr, &column_nr);
508 const String& line = String::Handle(zone(), script.GetLine(line_nr));
509 assembler()->Comment("Line %" Pd " in '%s':\n %s", line_nr,
510 instr->env()->function().ToFullyQualifiedCString(),
511 line.ToCString());
512}
513
514static bool IsPusher(Instruction* instr) {
515 if (auto def = instr->AsDefinition()) {
516 return def->HasTemp();
517 }
518 return false;
519}
520
521static bool IsPopper(Instruction* instr) {
522 // TODO(ajcbik): even allow deopt targets by making environment aware?
523 if (!instr->CanBecomeDeoptimizationTarget()) {
524 return !instr->IsPushArgument() && instr->ArgumentCount() == 0 &&
525 instr->InputCount() > 0;
526 }
527 return false;
528}
529
530bool FlowGraphCompiler::IsPeephole(Instruction* instr) const {
531 if (FLAG_enable_peephole && !is_optimizing()) {
532 return IsPusher(instr) && IsPopper(instr->next());
533 }
534 return false;
535}
536
537void FlowGraphCompiler::VisitBlocks() {
538 CompactBlocks();
539 if (compiler::Assembler::EmittingComments()) {
540 // The loop_info fields were cleared, recompute.
541 flow_graph().ComputeLoops();
542 }
543
544 // In precompiled mode, we require the function entry to come first (after the
545 // graph entry), since the polymorphic check is performed in the function
546 // entry (see Instructions::EntryPoint).
547 if (FLAG_precompiled_mode) {
548 ASSERT(block_order()[1] == flow_graph().graph_entry()->normal_entry());
549 }
550
551 for (intptr_t i = 0; i < block_order().length(); ++i) {
552 // Compile the block entry.
553 BlockEntryInstr* entry = block_order()[i];
554 assembler()->Comment("B%" Pd "", entry->block_id());
555 set_current_block(entry);
556
557 if (WasCompacted(entry)) {
558 continue;
559 }
560
561#if defined(DEBUG)
562 if (!is_optimizing()) {
563 FrameStateClear();
564 }
565#endif
566
567 if (compiler::Assembler::EmittingComments()) {
568 for (LoopInfo* l = entry->loop_info(); l != nullptr; l = l->outer()) {
569 assembler()->Comment(" Loop %" Pd "", l->id());
570 }
571 }
572
573 BeginCodeSourceRange();
574 ASSERT(pending_deoptimization_env_ == NULL);
575 pending_deoptimization_env_ = entry->env();
576 set_current_instruction(entry);
577 StatsBegin(entry);
578 entry->EmitNativeCode(this);
579 StatsEnd(entry);
580 set_current_instruction(nullptr);
581 pending_deoptimization_env_ = NULL;
582 EndCodeSourceRange(entry->token_pos());
583
584 if (skip_body_compilation()) {
585 ASSERT(entry == flow_graph().graph_entry()->normal_entry());
586 break;
587 }
588
589 // Compile all successors until an exit, branch, or a block entry.
590 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
591 Instruction* instr = it.Current();
592 set_current_instruction(instr);
593 StatsBegin(instr);
594
595 // Compose intervals.
596 code_source_map_builder_->StartInliningInterval(assembler()->CodeSize(),
597 instr->inlining_id());
598 if (FLAG_code_comments || FLAG_disassemble ||
599 FLAG_disassemble_optimized) {
600 if (FLAG_source_lines) {
601 EmitSourceLine(instr);
602 }
603 EmitComment(instr);
604 }
605 if (instr->IsParallelMove()) {
606 parallel_move_resolver_.EmitNativeCode(instr->AsParallelMove());
607 } else {
608 BeginCodeSourceRange();
609 EmitInstructionPrologue(instr);
610 ASSERT(pending_deoptimization_env_ == NULL);
611 pending_deoptimization_env_ = instr->env();
612 DEBUG_ONLY(current_instruction_ = instr);
613 instr->EmitNativeCode(this);
614 DEBUG_ONLY(current_instruction_ = nullptr);
615 pending_deoptimization_env_ = NULL;
616 if (IsPeephole(instr)) {
617 ASSERT(top_of_stack_ == nullptr);
618 top_of_stack_ = instr->AsDefinition();
619 } else {
620 EmitInstructionEpilogue(instr);
621 }
622 EndCodeSourceRange(instr->token_pos());
623 }
624
625#if defined(DEBUG)
626 if (!is_optimizing()) {
627 FrameStateUpdateWith(instr);
628 }
629#endif
630 StatsEnd(instr);
631 set_current_instruction(nullptr);
632
633 if (auto indirect_goto = instr->AsIndirectGoto()) {
634 indirect_gotos_.Add(indirect_goto);
635 }
636 }
637
638#if defined(DEBUG)
639 ASSERT(is_optimizing() || FrameStateIsSafeToCall());
640#endif
641 }
642
643 set_current_block(NULL);
644}
645
646void FlowGraphCompiler::Bailout(const char* reason) {
647 parsed_function_.Bailout("FlowGraphCompiler", reason);
648}
649
650intptr_t FlowGraphCompiler::StackSize() const {
651 if (is_optimizing_) {
652 return flow_graph_.graph_entry()->spill_slot_count();
653 } else {
654 return parsed_function_.num_stack_locals();
655 }
656}
657
658intptr_t FlowGraphCompiler::ExtraStackSlotsOnOsrEntry() const {
659 ASSERT(flow_graph().IsCompiledForOsr());
660 const intptr_t stack_depth =
661 flow_graph().graph_entry()->osr_entry()->stack_depth();
662 const intptr_t num_stack_locals = flow_graph().num_stack_locals();
663 return StackSize() - stack_depth - num_stack_locals;
664}
665
666compiler::Label* FlowGraphCompiler::GetJumpLabel(
667 BlockEntryInstr* block_entry) const {
668 const intptr_t block_index = block_entry->postorder_number();
669 return block_info_[block_index]->jump_label();
670}
671
672bool FlowGraphCompiler::WasCompacted(BlockEntryInstr* block_entry) const {
673 const intptr_t block_index = block_entry->postorder_number();
674 return block_info_[block_index]->WasCompacted();
675}
676
677compiler::Label* FlowGraphCompiler::NextNonEmptyLabel() const {
678 const intptr_t current_index = current_block()->postorder_number();
679 return block_info_[current_index]->next_nonempty_label();
680}
681
682bool FlowGraphCompiler::CanFallThroughTo(BlockEntryInstr* block_entry) const {
683 return NextNonEmptyLabel() == GetJumpLabel(block_entry);
684}
685
686BranchLabels FlowGraphCompiler::CreateBranchLabels(BranchInstr* branch) const {
687 compiler::Label* true_label = GetJumpLabel(branch->true_successor());
688 compiler::Label* false_label = GetJumpLabel(branch->false_successor());
689 compiler::Label* fall_through = NextNonEmptyLabel();
690 BranchLabels result = {true_label, false_label, fall_through};
691 return result;
692}
693
694void FlowGraphCompiler::AddSlowPathCode(SlowPathCode* code) {
695 slow_path_code_.Add(code);
696}
697
698void FlowGraphCompiler::GenerateDeferredCode() {
699 for (intptr_t i = 0; i < slow_path_code_.length(); i++) {
700 SlowPathCode* const slow_path = slow_path_code_[i];
701 const CombinedCodeStatistics::EntryCounter stats_tag =
702 CombinedCodeStatistics::SlowPathCounterFor(
703 slow_path->instruction()->tag());
704 set_current_instruction(slow_path->instruction());
705 SpecialStatsBegin(stats_tag);
706 BeginCodeSourceRange();
707 DEBUG_ONLY(current_instruction_ = slow_path->instruction());
708 slow_path->GenerateCode(this);
709 DEBUG_ONLY(current_instruction_ = nullptr);
710 EndCodeSourceRange(slow_path->instruction()->token_pos());
711 SpecialStatsEnd(stats_tag);
712 set_current_instruction(nullptr);
713 }
714 for (intptr_t i = 0; i < deopt_infos_.length(); i++) {
715 BeginCodeSourceRange();
716 deopt_infos_[i]->GenerateCode(this, i);
717 EndCodeSourceRange(TokenPosition::kDeferredDeoptInfo);
718 }
719}
720
721void FlowGraphCompiler::AddExceptionHandler(intptr_t try_index,
722 intptr_t outer_try_index,
723 intptr_t pc_offset,
724 bool is_generated,
725 const Array& handler_types,
726 bool needs_stacktrace) {
727 exception_handlers_list_->AddHandler(try_index, outer_try_index, pc_offset,
728 is_generated, handler_types,
729 needs_stacktrace);
730}
731
732void FlowGraphCompiler::SetNeedsStackTrace(intptr_t try_index) {
733 exception_handlers_list_->SetNeedsStackTrace(try_index);
734}
735
736void FlowGraphCompiler::AddDescriptor(PcDescriptorsLayout::Kind kind,
737 intptr_t pc_offset,
738 intptr_t deopt_id,
739 TokenPosition token_pos,
740 intptr_t try_index,
741 intptr_t yield_index) {
742 code_source_map_builder_->NoteDescriptor(kind, pc_offset, token_pos);
743 // Don't emit deopt-descriptors in AOT mode.
744 if (FLAG_precompiled_mode && (kind == PcDescriptorsLayout::kDeopt)) return;
745 pc_descriptors_list_->AddDescriptor(kind, pc_offset, deopt_id, token_pos,
746 try_index, yield_index);
747}
748
749// Uses current pc position and try-index.
750void FlowGraphCompiler::AddCurrentDescriptor(PcDescriptorsLayout::Kind kind,
751 intptr_t deopt_id,
752 TokenPosition token_pos) {
753 AddDescriptor(kind, assembler()->CodeSize(), deopt_id, token_pos,
754 CurrentTryIndex());
755}
756
757void FlowGraphCompiler::AddNullCheck(TokenPosition token_pos,
758 const String& name) {
759#if defined(DART_PRECOMPILER)
760 // If we are generating an AOT snapshot and have DWARF stack traces enabled,
761 // the AOT runtime is unable to obtain the pool index at runtime. Therefore,
762 // there is no reason to put the name into the pool in the first place.
763 // TODO(dartbug.com/40605): Move this info to the pc descriptors.
764 if (FLAG_precompiled_mode && FLAG_dwarf_stack_traces_mode) return;
765#endif
766 const intptr_t name_index =
767 assembler()->object_pool_builder().FindObject(name);
768 code_source_map_builder_->NoteNullCheck(assembler()->CodeSize(), token_pos,
769 name_index);
770}
771
772void FlowGraphCompiler::AddPcRelativeCallTarget(const Function& function,
773 Code::EntryKind entry_kind) {
774 ASSERT(function.IsZoneHandle());
775 const auto entry_point = entry_kind == Code::EntryKind::kUnchecked
776 ? Code::kUncheckedEntry
777 : Code::kDefaultEntry;
778 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
779 Code::kPcRelativeCall, entry_point, assembler()->CodeSize(), &function,
780 nullptr, nullptr));
781}
782
783void FlowGraphCompiler::AddPcRelativeCallStubTarget(const Code& stub_code) {
784 ASSERT(stub_code.IsZoneHandle() || stub_code.IsReadOnlyHandle());
785 ASSERT(!stub_code.IsNull());
786 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
787 Code::kPcRelativeCall, Code::kDefaultEntry, assembler()->CodeSize(),
788 nullptr, &stub_code, nullptr));
789}
790
791void FlowGraphCompiler::AddPcRelativeTailCallStubTarget(const Code& stub_code) {
792 ASSERT(stub_code.IsZoneHandle() || stub_code.IsReadOnlyHandle());
793 ASSERT(!stub_code.IsNull());
794 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
795 Code::kPcRelativeTailCall, Code::kDefaultEntry, assembler()->CodeSize(),
796 nullptr, &stub_code, nullptr));
797}
798
799void FlowGraphCompiler::AddPcRelativeTTSCallTypeTarget(
800 const AbstractType& dst_type) {
801 ASSERT(dst_type.IsZoneHandle() || dst_type.IsReadOnlyHandle());
802 ASSERT(!dst_type.IsNull());
803 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
804 Code::kPcRelativeTTSCall, Code::kDefaultEntry, assembler()->CodeSize(),
805 nullptr, nullptr, &dst_type));
806}
807
808void FlowGraphCompiler::AddStaticCallTarget(const Function& func,
809 Code::EntryKind entry_kind) {
810 ASSERT(func.IsZoneHandle());
811 const auto entry_point = entry_kind == Code::EntryKind::kUnchecked
812 ? Code::kUncheckedEntry
813 : Code::kDefaultEntry;
814 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
815 Code::kCallViaCode, entry_point, assembler()->CodeSize(), &func, nullptr,
816 nullptr));
817}
818
819void FlowGraphCompiler::AddStubCallTarget(const Code& code) {
820 ASSERT(code.IsZoneHandle() || code.IsReadOnlyHandle());
821 static_calls_target_table_.Add(new (zone()) StaticCallsStruct(
822 Code::kCallViaCode, Code::kDefaultEntry, assembler()->CodeSize(), nullptr,
823 &code, nullptr));
824}
825
826void FlowGraphCompiler::AddDispatchTableCallTarget(
827 const compiler::TableSelector* selector) {
828 dispatch_table_call_targets_.Add(selector);
829}
830
831CompilerDeoptInfo* FlowGraphCompiler::AddDeoptIndexAtCall(intptr_t deopt_id) {
832 ASSERT(is_optimizing());
833 ASSERT(!intrinsic_mode());
834 ASSERT(!FLAG_precompiled_mode);
835 CompilerDeoptInfo* info =
836 new (zone()) CompilerDeoptInfo(deopt_id, ICData::kDeoptAtCall,
837 0, // No flags.
838 pending_deoptimization_env_);
839 info->set_pc_offset(assembler()->CodeSize());
840 deopt_infos_.Add(info);
841 return info;
842}
843
844CompilerDeoptInfo* FlowGraphCompiler::AddSlowPathDeoptInfo(intptr_t deopt_id,
845 Environment* env) {
846 ASSERT(deopt_id != DeoptId::kNone);
847 CompilerDeoptInfo* info =
848 new (zone()) CompilerDeoptInfo(deopt_id, ICData::kDeoptUnknown, 0, env);
849 info->set_pc_offset(assembler()->CodeSize());
850 deopt_infos_.Add(info);
851 return info;
852}
853
854// This function must be in sync with FlowGraphCompiler::SaveLiveRegisters
855// and FlowGraphCompiler::SlowPathEnvironmentFor.
856// See StackFrame::VisitObjectPointers for the details of how stack map is
857// interpreted.
858void FlowGraphCompiler::RecordSafepoint(LocationSummary* locs,
859 intptr_t slow_path_argument_count) {
860 if (is_optimizing() || locs->live_registers()->HasUntaggedValues()) {
861 const intptr_t spill_area_size =
862 is_optimizing() ? flow_graph_.graph_entry()->spill_slot_count() : 0;
863
864 RegisterSet* registers = locs->live_registers();
865 ASSERT(registers != NULL);
866 const intptr_t kFpuRegisterSpillFactor =
867 kFpuRegisterSize / compiler::target::kWordSize;
868 intptr_t saved_registers_size = 0;
869 const bool using_shared_stub = locs->call_on_shared_slow_path();
870 if (using_shared_stub) {
871 saved_registers_size =
872 Utils::CountOneBitsWord(kDartAvailableCpuRegs) +
873 (registers->FpuRegisterCount() > 0
874 ? kFpuRegisterSpillFactor * kNumberOfFpuRegisters
875 : 0) +
876 1 /*saved PC*/;
877 } else {
878 saved_registers_size =
879 registers->CpuRegisterCount() +
880 (registers->FpuRegisterCount() * kFpuRegisterSpillFactor);
881 }
882
883 BitmapBuilder* bitmap = locs->stack_bitmap();
884
885 // An instruction may have two safepoints in deferred code. The
886 // call to RecordSafepoint has the side-effect of appending the live
887 // registers to the bitmap. This is why the second call to RecordSafepoint
888 // with the same instruction (and same location summary) sees a bitmap that
889 // is larger that StackSize(). It will never be larger than StackSize() +
890 // unboxed_arg_bits_count + live_registers_size.
891 // The first safepoint will grow the bitmap to be the size of
892 // spill_area_size but the second safepoint will truncate the bitmap and
893 // append the bits for arguments and live registers to it again.
894 const intptr_t bitmap_previous_length = bitmap->Length();
895 bitmap->SetLength(spill_area_size);
896
897 intptr_t unboxed_arg_bits_count = 0;
898
899 auto instr = current_instruction();
900 const intptr_t args_count = instr->ArgumentCount();
901 bool pushed_unboxed = false;
902
903 for (intptr_t i = 0; i < args_count; i++) {
904 auto push_arg =
905 instr->ArgumentValueAt(i)->instruction()->AsPushArgument();
906 switch (push_arg->representation()) {
907 case kUnboxedInt64:
908 bitmap->SetRange(
909 bitmap->Length(),
910 bitmap->Length() + compiler::target::kIntSpillFactor - 1, false);
911 unboxed_arg_bits_count += compiler::target::kIntSpillFactor;
912 pushed_unboxed = true;
913 break;
914 case kUnboxedDouble:
915 bitmap->SetRange(
916 bitmap->Length(),
917 bitmap->Length() + compiler::target::kDoubleSpillFactor - 1,
918 false);
919 unboxed_arg_bits_count += compiler::target::kDoubleSpillFactor;
920 pushed_unboxed = true;
921 break;
922 case kTagged:
923 if (!pushed_unboxed) {
924 // GC considers everything to be tagged between prefix of stack
925 // frame (spill area size) and postfix of stack frame (e.g. slow
926 // path arguments, shared pushed registers).
927 // From the first unboxed argument on we will include bits in the
928 // postfix.
929 continue;
930 }
931 bitmap->Set(bitmap->Length(), true);
932 unboxed_arg_bits_count++;
933 break;
934 default:
935 UNREACHABLE();
936 break;
937 }
938 }
939 ASSERT(bitmap_previous_length <=
940 (spill_area_size + unboxed_arg_bits_count + saved_registers_size));
941
942 ASSERT(slow_path_argument_count == 0 || !using_shared_stub);
943
944 // Mark the bits in the stack map in the same order we push registers in
945 // slow path code (see FlowGraphCompiler::SaveLiveRegisters).
946 //
947 // Slow path code can have registers at the safepoint.
948 if (!locs->always_calls() && !using_shared_stub) {
949 RegisterSet* regs = locs->live_registers();
950 if (regs->FpuRegisterCount() > 0) {
951 // Denote FPU registers with 0 bits in the stackmap. Based on the
952 // assumption that there are normally few live FPU registers, this
953 // encoding is simpler and roughly as compact as storing a separate
954 // count of FPU registers.
955 //
956 // FPU registers have the highest register number at the highest
957 // address (i.e., first in the stackmap).
958 for (intptr_t i = kNumberOfFpuRegisters - 1; i >= 0; --i) {
959 FpuRegister reg = static_cast<FpuRegister>(i);
960 if (regs->ContainsFpuRegister(reg)) {
961 for (intptr_t j = 0; j < kFpuRegisterSpillFactor; ++j) {
962 bitmap->Set(bitmap->Length(), false);
963 }
964 }
965 }
966 }
967
968 // General purpose registers have the highest register number at the
969 // highest address (i.e., first in the stackmap).
970 for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; --i) {
971 Register reg = static_cast<Register>(i);
972 if (locs->live_registers()->ContainsRegister(reg)) {
973 bitmap->Set(bitmap->Length(), locs->live_registers()->IsTagged(reg));
974 }
975 }
976 }
977
978 if (using_shared_stub) {
979 // To simplify the code in the shared stub, we create an untagged hole
980 // in the stack frame where the shared stub can leave the return address
981 // before saving registers.
982 bitmap->Set(bitmap->Length(), false);
983 if (registers->FpuRegisterCount() > 0) {
984 bitmap->SetRange(bitmap->Length(),
985 bitmap->Length() +
986 kNumberOfFpuRegisters * kFpuRegisterSpillFactor -
987 1,
988 false);
989 }
990 for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; --i) {
991 if ((kReservedCpuRegisters & (1 << i)) != 0) continue;
992 const Register reg = static_cast<Register>(i);
993 bitmap->Set(bitmap->Length(),
994 locs->live_registers()->ContainsRegister(reg) &&
995 locs->live_registers()->IsTagged(reg));
996 }
997 }
998
999 // Arguments pushed after live registers in the slow path are tagged.
1000 for (intptr_t i = 0; i < slow_path_argument_count; ++i) {
1001 bitmap->Set(bitmap->Length(), true);
1002 }
1003
1004 compressed_stackmaps_builder()->AddEntry(assembler()->CodeSize(), bitmap,
1005 spill_area_size);
1006 }
1007}
1008
1009// This function must be kept in sync with:
1010//
1011// FlowGraphCompiler::RecordSafepoint
1012// FlowGraphCompiler::SaveLiveRegisters
1013// MaterializeObjectInstr::RemapRegisters
1014//
1015Environment* FlowGraphCompiler::SlowPathEnvironmentFor(
1016 Environment* env,
1017 LocationSummary* locs,
1018 intptr_t num_slow_path_args) {
1019 const bool using_shared_stub = locs->call_on_shared_slow_path();
1020 const bool shared_stub_save_fpu_registers =
1021 using_shared_stub && locs->live_registers()->FpuRegisterCount() > 0;
1022 // TODO(sjindel): Modify logic below to account for slow-path args with shared
1023 // stubs.
1024 ASSERT(!using_shared_stub || num_slow_path_args == 0);
1025 if (env == nullptr) {
1026 // In AOT, environments can be removed by EliminateEnvironments pass
1027 // (if not in a try block).
1028 ASSERT(!is_optimizing() || FLAG_precompiled_mode);
1029 return nullptr;
1030 }
1031
1032 Environment* slow_path_env = env->DeepCopy(zone());
1033 // 1. Iterate the registers in the order they will be spilled to compute
1034 // the slots they will be spilled to.
1035 intptr_t next_slot = StackSize() + slow_path_env->CountArgsPushed();
1036 if (using_shared_stub) {
1037 // The PC from the call to the shared stub is pushed here.
1038 next_slot++;
1039 }
1040 RegisterSet* regs = locs->live_registers();
1041 intptr_t fpu_reg_slots[kNumberOfFpuRegisters];
1042 intptr_t cpu_reg_slots[kNumberOfCpuRegisters];
1043 const intptr_t kFpuRegisterSpillFactor =
1044 kFpuRegisterSize / compiler::target::kWordSize;
1045 // FPU registers are spilled first from highest to lowest register number.
1046 for (intptr_t i = kNumberOfFpuRegisters - 1; i >= 0; --i) {
1047 FpuRegister reg = static_cast<FpuRegister>(i);
1048 if (regs->ContainsFpuRegister(reg)) {
1049 // We use the lowest address (thus highest index) to identify a
1050 // multi-word spill slot.
1051 next_slot += kFpuRegisterSpillFactor;
1052 fpu_reg_slots[i] = (next_slot - 1);
1053 } else {
1054 if (using_shared_stub && shared_stub_save_fpu_registers) {
1055 next_slot += kFpuRegisterSpillFactor;
1056 }
1057 fpu_reg_slots[i] = -1;
1058 }
1059 }
1060 // General purpose registers are spilled from highest to lowest register
1061 // number.
1062 for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; --i) {
1063 if ((kReservedCpuRegisters & (1 << i)) != 0) continue;
1064 Register reg = static_cast<Register>(i);
1065 if (regs->ContainsRegister(reg)) {
1066 cpu_reg_slots[i] = next_slot++;
1067 } else {
1068 if (using_shared_stub) next_slot++;
1069 cpu_reg_slots[i] = -1;
1070 }
1071 }
1072
1073 // 2. Iterate the environment and replace register locations with the
1074 // corresponding spill slot locations.
1075 for (Environment::DeepIterator it(slow_path_env); !it.Done(); it.Advance()) {
1076 Location loc = it.CurrentLocation();
1077 Value* value = it.CurrentValue();
1078 it.SetCurrentLocation(LocationRemapForSlowPath(
1079 loc, value->definition(), cpu_reg_slots, fpu_reg_slots));
1080 }
1081
1082 return slow_path_env;
1083}
1084
1085compiler::Label* FlowGraphCompiler::AddDeoptStub(intptr_t deopt_id,
1086 ICData::DeoptReasonId reason,
1087 uint32_t flags) {
1088 if (intrinsic_mode()) {
1089 return intrinsic_slow_path_label_;
1090 }
1091
1092 // No deoptimization allowed when 'FLAG_precompiled_mode' is set.
1093 if (FLAG_precompiled_mode) {
1094 if (FLAG_trace_compiler) {
1095 THR_Print(
1096 "Retrying compilation %s, suppressing inlining of deopt_id:%" Pd "\n",
1097 parsed_function_.function().ToFullyQualifiedCString(), deopt_id);
1098 }
1099 ASSERT(speculative_policy_->AllowsSpeculativeInlining());
1100 ASSERT(deopt_id != 0); // longjmp must return non-zero value.
1101 Thread::Current()->long_jump_base()->Jump(
1102 deopt_id, Object::speculative_inlining_error());
1103 }
1104
1105 ASSERT(is_optimizing_);
1106 CompilerDeoptInfoWithStub* stub = new (zone()) CompilerDeoptInfoWithStub(
1107 deopt_id, reason, flags, pending_deoptimization_env_);
1108 deopt_infos_.Add(stub);
1109 return stub->entry_label();
1110}
1111
1112void FlowGraphCompiler::FinalizeExceptionHandlers(const Code& code) {
1113 ASSERT(exception_handlers_list_ != NULL);
1114 const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
1115 exception_handlers_list_->FinalizeExceptionHandlers(code.PayloadStart()));
1116 code.set_exception_handlers(handlers);
1117}
1118
1119void FlowGraphCompiler::FinalizePcDescriptors(const Code& code) {
1120 ASSERT(pc_descriptors_list_ != NULL);
1121 const PcDescriptors& descriptors = PcDescriptors::Handle(
1122 pc_descriptors_list_->FinalizePcDescriptors(code.PayloadStart()));
1123 if (!is_optimizing_) descriptors.Verify(parsed_function_.function());
1124 code.set_pc_descriptors(descriptors);
1125}
1126
1127ArrayPtr FlowGraphCompiler::CreateDeoptInfo(compiler::Assembler* assembler) {
1128 // No deopt information if we precompile (no deoptimization allowed).
1129 if (FLAG_precompiled_mode) {
1130 return Array::empty_array().raw();
1131 }
1132 // For functions with optional arguments, all incoming arguments are copied
1133 // to spill slots. The deoptimization environment does not track them.
1134 const Function& function = parsed_function().function();
1135 const intptr_t incoming_arg_count =
1136 function.HasOptionalParameters() ? 0 : function.num_fixed_parameters();
1137 DeoptInfoBuilder builder(zone(), incoming_arg_count, assembler);
1138
1139 intptr_t deopt_info_table_size = DeoptTable::SizeFor(deopt_infos_.length());
1140 if (deopt_info_table_size == 0) {
1141 return Object::empty_array().raw();
1142 } else {
1143 const Array& array =
1144 Array::Handle(Array::New(deopt_info_table_size, Heap::kOld));
1145 Smi& offset = Smi::Handle();
1146 TypedData& info = TypedData::Handle();
1147 Smi& reason_and_flags = Smi::Handle();
1148 for (intptr_t i = 0; i < deopt_infos_.length(); i++) {
1149 offset = Smi::New(deopt_infos_[i]->pc_offset());
1150 info = deopt_infos_[i]->CreateDeoptInfo(this, &builder, array);
1151 reason_and_flags = DeoptTable::EncodeReasonAndFlags(
1152 deopt_infos_[i]->reason(), deopt_infos_[i]->flags());
1153 DeoptTable::SetEntry(array, i, offset, info, reason_and_flags);
1154 }
1155 return array.raw();
1156 }
1157}
1158
1159void FlowGraphCompiler::FinalizeStackMaps(const Code& code) {
1160 if (compressed_stackmaps_builder_ == NULL) {
1161 code.set_compressed_stackmaps(
1162 CompressedStackMaps::Handle(CompressedStackMaps::null()));
1163 } else {
1164 // Finalize the compressed stack maps and add it to the code object.
1165 const auto& maps =
1166 CompressedStackMaps::Handle(compressed_stackmaps_builder_->Finalize());
1167 code.set_compressed_stackmaps(maps);
1168 }
1169}
1170
1171void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) {
1172#if defined(PRODUCT)
1173// No debugger: no var descriptors.
1174#else
1175 if (code.is_optimized()) {
1176 // Optimized code does not need variable descriptors. They are
1177 // only stored in the unoptimized version.
1178 code.set_var_descriptors(Object::empty_var_descriptors());
1179 return;
1180 }
1181 LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle();
1182 if (flow_graph().IsIrregexpFunction()) {
1183 // Eager local var descriptors computation for Irregexp function as it is
1184 // complicated to factor out.
1185 // TODO(srdjan): Consider canonicalizing and reusing the local var
1186 // descriptor for IrregexpFunction.
1187 ASSERT(parsed_function().scope() == nullptr);
1188 var_descs = LocalVarDescriptors::New(1);
1189 LocalVarDescriptorsLayout::VarInfo info;
1190 info.set_kind(LocalVarDescriptorsLayout::kSavedCurrentContext);
1191 info.scope_id = 0;
1192 info.begin_pos = TokenPosition::kMinSource;
1193 info.end_pos = TokenPosition::kMinSource;
1194 info.set_index(compiler::target::frame_layout.FrameSlotForVariable(
1195 parsed_function().current_context_var()));
1196 var_descs.SetVar(0, Symbols::CurrentContextVar(), &info);
1197 }
1198 code.set_var_descriptors(var_descs);
1199#endif
1200}
1201
1202void FlowGraphCompiler::FinalizeCatchEntryMovesMap(const Code& code) {
1203#if defined(DART_PRECOMPILER)
1204 if (FLAG_precompiled_mode) {
1205 TypedData& maps = TypedData::Handle(
1206 catch_entry_moves_maps_builder_->FinalizeCatchEntryMovesMap());
1207 code.set_catch_entry_moves_maps(maps);
1208 return;
1209 }
1210#endif
1211 code.set_num_variables(flow_graph().variable_count());
1212}
1213
1214void FlowGraphCompiler::FinalizeStaticCallTargetsTable(const Code& code) {
1215 ASSERT(code.static_calls_target_table() == Array::null());
1216 const auto& calls = static_calls_target_table_;
1217 const intptr_t array_length = calls.length() * Code::kSCallTableEntryLength;
1218 const auto& targets =
1219 Array::Handle(zone(), Array::New(array_length, Heap::kOld));
1220
1221 StaticCallsTable entries(targets);
1222 auto& kind_type_and_offset = Smi::Handle(zone());
1223 for (intptr_t i = 0; i < calls.length(); i++) {
1224 auto entry = calls[i];
1225 kind_type_and_offset =
1226 Smi::New(Code::KindField::encode(entry->call_kind) |
1227 Code::EntryPointField::encode(entry->entry_point) |
1228 Code::OffsetField::encode(entry->offset));
1229 auto view = entries[i];
1230 view.Set<Code::kSCallTableKindAndOffset>(kind_type_and_offset);
1231 const Object* target = nullptr;
1232 if (entry->function != nullptr) {
1233 target = entry->function;
1234 view.Set<Code::kSCallTableFunctionTarget>(*entry->function);
1235 }
1236 if (entry->code != nullptr) {
1237 ASSERT(target == nullptr);
1238 target = entry->code;
1239 view.Set<Code::kSCallTableCodeOrTypeTarget>(*entry->code);
1240 }
1241 if (entry->dst_type != nullptr) {
1242 ASSERT(target == nullptr);
1243 view.Set<Code::kSCallTableCodeOrTypeTarget>(*entry->dst_type);
1244 }
1245 }
1246 code.set_static_calls_target_table(targets);
1247}
1248
1249void FlowGraphCompiler::FinalizeCodeSourceMap(const Code& code) {
1250 const Array& inlined_id_array =
1251 Array::Handle(zone(), code_source_map_builder_->InliningIdToFunction());
1252 code.set_inlined_id_to_function(inlined_id_array);
1253
1254 const CodeSourceMap& map =
1255 CodeSourceMap::Handle(code_source_map_builder_->Finalize());
1256 code.set_code_source_map(map);
1257
1258#if defined(DEBUG)
1259 // Force simulation through the last pc offset. This checks we can decode
1260 // the whole CodeSourceMap without hitting an unknown opcode, stack underflow,
1261 // etc.
1262 GrowableArray<const Function*> fs;
1263 GrowableArray<TokenPosition> tokens;
1264 code.GetInlinedFunctionsAtInstruction(code.Size() - 1, &fs, &tokens);
1265#endif
1266}
1267
1268// Returns 'true' if regular code generation should be skipped.
1269bool FlowGraphCompiler::TryIntrinsify() {
1270 if (TryIntrinsifyHelper()) {
1271 fully_intrinsified_ = true;
1272 return true;
1273 }
1274 return false;
1275}
1276
1277bool FlowGraphCompiler::TryIntrinsifyHelper() {
1278 compiler::Label exit;
1279 set_intrinsic_slow_path_label(&exit);
1280
1281 if (FLAG_intrinsify) {
1282 const auto& function = parsed_function().function();
1283 if (function.IsMethodExtractor()) {
1284#if !defined(TARGET_ARCH_IA32)
1285 auto& extracted_method =
1286 Function::ZoneHandle(function.extracted_method_closure());
1287 auto& klass = Class::Handle(extracted_method.Owner());
1288 const intptr_t type_arguments_field_offset =
1289 compiler::target::Class::HasTypeArgumentsField(klass)
1290 ? (compiler::target::Class::TypeArgumentsFieldOffset(klass) -
1291 kHeapObjectTag)
1292 : 0;
1293
1294 SpecialStatsBegin(CombinedCodeStatistics::kTagIntrinsics);
1295 GenerateMethodExtractorIntrinsic(extracted_method,
1296 type_arguments_field_offset);
1297 SpecialStatsEnd(CombinedCodeStatistics::kTagIntrinsics);
1298 return true;
1299#endif // !defined(TARGET_ARCH_IA32)
1300 }
1301 }
1302
1303 EnterIntrinsicMode();
1304
1305 SpecialStatsBegin(CombinedCodeStatistics::kTagIntrinsics);
1306 bool complete = compiler::Intrinsifier::Intrinsify(parsed_function(), this);
1307 SpecialStatsEnd(CombinedCodeStatistics::kTagIntrinsics);
1308
1309 ExitIntrinsicMode();
1310
1311 // "Deoptimization" from intrinsic continues here. All deoptimization
1312 // branches from intrinsic code redirect to here where the slow-path
1313 // (normal function body) starts.
1314 // This means that there must not be any side-effects in intrinsic code
1315 // before any deoptimization point.
1316 assembler()->Bind(intrinsic_slow_path_label());
1317 set_intrinsic_slow_path_label(nullptr);
1318 return complete;
1319}
1320
1321void FlowGraphCompiler::GenerateStubCall(TokenPosition token_pos,
1322 const Code& stub,
1323 PcDescriptorsLayout::Kind kind,
1324 LocationSummary* locs,
1325 intptr_t deopt_id,
1326 Environment* env) {
1327 EmitCallToStub(stub);
1328 EmitCallsiteMetadata(token_pos, deopt_id, kind, locs, env);
1329}
1330
1331static const Code& StubEntryFor(const ICData& ic_data, bool optimized) {
1332 switch (ic_data.NumArgsTested()) {
1333 case 1:
1334#if defined(TARGET_ARCH_X64)
1335 if (ic_data.is_tracking_exactness()) {
1336 if (optimized) {
1337 return StubCode::OneArgOptimizedCheckInlineCacheWithExactnessCheck();
1338 } else {
1339 return StubCode::OneArgCheckInlineCacheWithExactnessCheck();
1340 }
1341 }
1342#else
1343 // TODO(dartbug.com/34170) Port exactness tracking to other platforms.
1344 ASSERT(!ic_data.is_tracking_exactness());
1345#endif
1346 return optimized ? StubCode::OneArgOptimizedCheckInlineCache()
1347 : StubCode::OneArgCheckInlineCache();
1348 case 2:
1349 ASSERT(!ic_data.is_tracking_exactness());
1350 return optimized ? StubCode::TwoArgsOptimizedCheckInlineCache()
1351 : StubCode::TwoArgsCheckInlineCache();
1352 default:
1353 ic_data.Print();
1354 UNIMPLEMENTED();
1355 return Code::Handle();
1356 }
1357}
1358
1359void FlowGraphCompiler::GenerateInstanceCall(intptr_t deopt_id,
1360 TokenPosition token_pos,
1361 LocationSummary* locs,
1362 const ICData& ic_data_in,
1363 Code::EntryKind entry_kind,
1364 bool receiver_can_be_smi) {
1365 ICData& ic_data = ICData::ZoneHandle(ic_data_in.Original());
1366 if (FLAG_precompiled_mode) {
1367 ic_data = ic_data.AsUnaryClassChecks();
1368 EmitInstanceCallAOT(ic_data, deopt_id, token_pos, locs, entry_kind,
1369 receiver_can_be_smi);
1370 return;
1371 }
1372 ASSERT(!ic_data.IsNull());
1373 if (is_optimizing() && (ic_data_in.NumberOfUsedChecks() == 0)) {
1374 // Emit IC call that will count and thus may need reoptimization at
1375 // function entry.
1376 ASSERT(may_reoptimize() || flow_graph().IsCompiledForOsr());
1377 EmitOptimizedInstanceCall(StubEntryFor(ic_data, /*optimized=*/true),
1378 ic_data, deopt_id, token_pos, locs, entry_kind);
1379 return;
1380 }
1381
1382 if (is_optimizing()) {
1383 EmitMegamorphicInstanceCall(ic_data_in, deopt_id, token_pos, locs,
1384 kInvalidTryIndex);
1385 return;
1386 }
1387
1388 EmitInstanceCallJIT(StubEntryFor(ic_data, /*optimized=*/false), ic_data,
1389 deopt_id, token_pos, locs, entry_kind);
1390}
1391
1392void FlowGraphCompiler::GenerateStaticCall(intptr_t deopt_id,
1393 TokenPosition token_pos,
1394 const Function& function,
1395 ArgumentsInfo args_info,
1396 LocationSummary* locs,
1397 const ICData& ic_data_in,
1398 ICData::RebindRule rebind_rule,
1399 Code::EntryKind entry_kind) {
1400 const ICData& ic_data = ICData::ZoneHandle(ic_data_in.Original());
1401 const Array& arguments_descriptor = Array::ZoneHandle(
1402 zone(), ic_data.IsNull() ? args_info.ToArgumentsDescriptor()
1403 : ic_data.arguments_descriptor());
1404 ASSERT(ArgumentsDescriptor(arguments_descriptor).TypeArgsLen() ==
1405 args_info.type_args_len);
1406 ASSERT(ArgumentsDescriptor(arguments_descriptor).Count() ==
1407 args_info.count_without_type_args);
1408 ASSERT(ArgumentsDescriptor(arguments_descriptor).Size() ==
1409 args_info.size_without_type_args);
1410 // Force-optimized functions lack the deopt info which allows patching of
1411 // optimized static calls.
1412 if (is_optimizing() && (!ForcedOptimization() || FLAG_precompiled_mode)) {
1413 EmitOptimizedStaticCall(function, arguments_descriptor,
1414 args_info.size_with_type_args, deopt_id, token_pos,
1415 locs, entry_kind);
1416 } else {
1417 ICData& call_ic_data = ICData::ZoneHandle(zone(), ic_data.raw());
1418 if (call_ic_data.IsNull()) {
1419 const intptr_t kNumArgsChecked = 0;
1420 call_ic_data =
1421 GetOrAddStaticCallICData(deopt_id, function, arguments_descriptor,
1422 kNumArgsChecked, rebind_rule)
1423 ->raw();
1424 call_ic_data = call_ic_data.Original();
1425 }
1426 AddCurrentDescriptor(PcDescriptorsLayout::kRewind, deopt_id, token_pos);
1427 EmitUnoptimizedStaticCall(args_info.size_with_type_args, deopt_id,
1428 token_pos, locs, call_ic_data, entry_kind);
1429 }
1430}
1431
1432void FlowGraphCompiler::GenerateNumberTypeCheck(
1433 Register class_id_reg,
1434 const AbstractType& type,
1435 compiler::Label* is_instance_lbl,
1436 compiler::Label* is_not_instance_lbl) {
1437 assembler()->Comment("NumberTypeCheck");
1438 GrowableArray<intptr_t> args;
1439 if (type.IsNumberType()) {
1440 args.Add(kDoubleCid);
1441 args.Add(kMintCid);
1442 } else if (type.IsIntType()) {
1443 args.Add(kMintCid);
1444 } else if (type.IsDoubleType()) {
1445 args.Add(kDoubleCid);
1446 }
1447 CheckClassIds(class_id_reg, args, is_instance_lbl, is_not_instance_lbl);
1448}
1449
1450void FlowGraphCompiler::GenerateStringTypeCheck(
1451 Register class_id_reg,
1452 compiler::Label* is_instance_lbl,
1453 compiler::Label* is_not_instance_lbl) {
1454 assembler()->Comment("StringTypeCheck");
1455 GrowableArray<intptr_t> args;
1456 args.Add(kOneByteStringCid);
1457 args.Add(kTwoByteStringCid);
1458 args.Add(kExternalOneByteStringCid);
1459 args.Add(kExternalTwoByteStringCid);
1460 CheckClassIds(class_id_reg, args, is_instance_lbl, is_not_instance_lbl);
1461}
1462
1463void FlowGraphCompiler::GenerateListTypeCheck(
1464 Register class_id_reg,
1465 compiler::Label* is_instance_lbl) {
1466 assembler()->Comment("ListTypeCheck");
1467 compiler::Label unknown;
1468 GrowableArray<intptr_t> args;
1469 args.Add(kArrayCid);
1470 args.Add(kGrowableObjectArrayCid);
1471 args.Add(kImmutableArrayCid);
1472 CheckClassIds(class_id_reg, args, is_instance_lbl, &unknown);
1473 assembler()->Bind(&unknown);
1474}
1475
1476void FlowGraphCompiler::EmitComment(Instruction* instr) {
1477 if (!FLAG_support_il_printer || !FLAG_support_disassembler) {
1478 return;
1479 }
1480#ifndef PRODUCT
1481 char buffer[256];
1482 BufferFormatter f(buffer, sizeof(buffer));
1483 instr->PrintTo(&f);
1484 assembler()->Comment("%s", buffer);
1485#endif
1486}
1487
1488bool FlowGraphCompiler::NeedsEdgeCounter(BlockEntryInstr* block) {
1489 // Only emit an edge counter if there is not goto at the end of the block,
1490 // except for the entry block.
1491 return FLAG_reorder_basic_blocks &&
1492 (!block->last_instruction()->IsGoto() || block->IsFunctionEntry());
1493}
1494
1495// Allocate a register that is not explictly blocked.
1496static Register AllocateFreeRegister(bool* blocked_registers) {
1497 for (intptr_t regno = 0; regno < kNumberOfCpuRegisters; regno++) {
1498 if (!blocked_registers[regno]) {
1499 blocked_registers[regno] = true;
1500 return static_cast<Register>(regno);
1501 }
1502 }
1503 UNREACHABLE();
1504 return kNoRegister;
1505}
1506
1507void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) {
1508 ASSERT(!is_optimizing());
1509 instr->InitializeLocationSummary(zone(), false); // Not optimizing.
1510
1511 LocationSummary* locs = instr->locs();
1512
1513 bool blocked_registers[kNumberOfCpuRegisters];
1514
1515 // Connect input with peephole output for some special cases. All other
1516 // cases are handled by simply allocating registers and generating code.
1517 if (top_of_stack_ != nullptr) {
1518 const intptr_t p = locs->input_count() - 1;
1519 Location peephole = top_of_stack_->locs()->out(0);
1520 if (locs->in(p).IsUnallocated() || locs->in(p).IsConstant()) {
1521 // If input is unallocated, match with an output register, if set. Also,
1522 // if input is a direct constant, but the peephole output is a register,
1523 // use that register to avoid wasting the already generated code.
1524 if (peephole.IsRegister()) {
1525 locs->set_in(p, Location::RegisterLocation(peephole.reg()));
1526 }
1527 }
1528 }
1529
1530 // Block all registers globally reserved by the assembler, etc and mark
1531 // the rest as free.
1532 for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
1533 blocked_registers[i] = (kDartAvailableCpuRegs & (1 << i)) == 0;
1534 }
1535
1536 // Mark all fixed input, temp and output registers as used.
1537 for (intptr_t i = 0; i < locs->input_count(); i++) {
1538 Location loc = locs->in(i);
1539 if (loc.IsRegister()) {
1540 // Check that a register is not specified twice in the summary.
1541 ASSERT(!blocked_registers[loc.reg()]);
1542 blocked_registers[loc.reg()] = true;
1543 }
1544 }
1545
1546 for (intptr_t i = 0; i < locs->temp_count(); i++) {
1547 Location loc = locs->temp(i);
1548 if (loc.IsRegister()) {
1549 // Check that a register is not specified twice in the summary.
1550 ASSERT(!blocked_registers[loc.reg()]);
1551 blocked_registers[loc.reg()] = true;
1552 }
1553 }
1554
1555 if (locs->out(0).IsRegister()) {
1556 // Fixed output registers are allowed to overlap with
1557 // temps and inputs.
1558 blocked_registers[locs->out(0).reg()] = true;
1559 }
1560
1561 // Allocate all unallocated input locations.
1562 const bool should_pop = !instr->IsPushArgument();
1563 for (intptr_t i = locs->input_count() - 1; i >= 0; i--) {
1564 Location loc = locs->in(i);
1565 Register reg = kNoRegister;
1566 if (loc.IsRegister()) {
1567 reg = loc.reg();
1568 } else if (loc.IsUnallocated()) {
1569 ASSERT((loc.policy() == Location::kRequiresRegister) ||
1570 (loc.policy() == Location::kWritableRegister) ||
1571 (loc.policy() == Location::kPrefersRegister) ||
1572 (loc.policy() == Location::kAny));
1573 reg = AllocateFreeRegister(blocked_registers);
1574 locs->set_in(i, Location::RegisterLocation(reg));
1575 }
1576 ASSERT(reg != kNoRegister || loc.IsConstant());
1577
1578 // Inputs are consumed from the simulated frame (or a peephole push/pop).
1579 // In case of a call argument we leave it until the call instruction.
1580 if (should_pop) {
1581 if (top_of_stack_ != nullptr) {
1582 if (!loc.IsConstant()) {
1583 // Moves top of stack location of the peephole into the required
1584 // input. None of the required moves needs a temp register allocator.
1585 EmitMove(locs->in(i), top_of_stack_->locs()->out(0), nullptr);
1586 }
1587 top_of_stack_ = nullptr; // consumed!
1588 } else if (loc.IsConstant()) {
1589 assembler()->Drop(1);
1590 } else {
1591 assembler()->PopRegister(reg);
1592 }
1593 }
1594 }
1595
1596 // Allocate all unallocated temp locations.
1597 for (intptr_t i = 0; i < locs->temp_count(); i++) {
1598 Location loc = locs->temp(i);
1599 if (loc.IsUnallocated()) {
1600 ASSERT(loc.policy() == Location::kRequiresRegister);
1601 loc = Location::RegisterLocation(AllocateFreeRegister(blocked_registers));
1602 locs->set_temp(i, loc);
1603 }
1604 }
1605
1606 Location result_location = locs->out(0);
1607 if (result_location.IsUnallocated()) {
1608 switch (result_location.policy()) {
1609 case Location::kAny:
1610 case Location::kPrefersRegister:
1611 case Location::kRequiresRegister:
1612 case Location::kWritableRegister:
1613 result_location =
1614 Location::RegisterLocation(AllocateFreeRegister(blocked_registers));
1615 break;
1616 case Location::kSameAsFirstInput:
1617 result_location = locs->in(0);
1618 break;
1619 case Location::kRequiresFpuRegister:
1620 UNREACHABLE();
1621 break;
1622 }
1623 locs->set_out(0, result_location);
1624 }
1625}
1626
1627static uword RegMaskBit(Register reg) {
1628 return ((reg) != kNoRegister) ? (1 << (reg)) : 0;
1629}
1630
1631ParallelMoveResolver::ParallelMoveResolver(FlowGraphCompiler* compiler)
1632 : compiler_(compiler), moves_(32) {}
1633
1634void ParallelMoveResolver::EmitNativeCode(ParallelMoveInstr* parallel_move) {
1635 ASSERT(moves_.is_empty());
1636 // Build up a worklist of moves.
1637 BuildInitialMoveList(parallel_move);
1638
1639 for (int i = 0; i < moves_.length(); ++i) {
1640 const MoveOperands& move = *moves_[i];
1641 // Skip constants to perform them last. They don't block other moves
1642 // and skipping such moves with register destinations keeps those
1643 // registers free for the whole algorithm.
1644 if (!move.IsEliminated() && !move.src().IsConstant()) PerformMove(i);
1645 }
1646
1647 // Perform the moves with constant sources.
1648 for (int i = 0; i < moves_.length(); ++i) {
1649 const MoveOperands& move = *moves_[i];
1650 if (!move.IsEliminated()) {
1651 ASSERT(move.src().IsConstant());
1652 compiler_->BeginCodeSourceRange();
1653 EmitMove(i);
1654 compiler_->EndCodeSourceRange(TokenPosition::kParallelMove);
1655 }
1656 }
1657
1658 moves_.Clear();
1659}
1660
1661void ParallelMoveResolver::BuildInitialMoveList(
1662 ParallelMoveInstr* parallel_move) {
1663 // Perform a linear sweep of the moves to add them to the initial list of
1664 // moves to perform, ignoring any move that is redundant (the source is
1665 // the same as the destination, the destination is ignored and
1666 // unallocated, or the move was already eliminated).
1667 for (int i = 0; i < parallel_move->NumMoves(); i++) {
1668 MoveOperands* move = parallel_move->MoveOperandsAt(i);
1669 if (!move->IsRedundant()) moves_.Add(move);
1670 }
1671}
1672
1673void ParallelMoveResolver::PerformMove(int index) {
1674 // Each call to this function performs a move and deletes it from the move
1675 // graph. We first recursively perform any move blocking this one. We
1676 // mark a move as "pending" on entry to PerformMove in order to detect
1677 // cycles in the move graph. We use operand swaps to resolve cycles,
1678 // which means that a call to PerformMove could change any source operand
1679 // in the move graph.
1680
1681 ASSERT(!moves_[index]->IsPending());
1682 ASSERT(!moves_[index]->IsRedundant());
1683
1684 // Clear this move's destination to indicate a pending move. The actual
1685 // destination is saved in a stack-allocated local. Recursion may allow
1686 // multiple moves to be pending.
1687 ASSERT(!moves_[index]->src().IsInvalid());
1688 Location destination = moves_[index]->MarkPending();
1689
1690 // Perform a depth-first traversal of the move graph to resolve
1691 // dependencies. Any unperformed, unpending move with a source the same
1692 // as this one's destination blocks this one so recursively perform all
1693 // such moves.
1694 for (int i = 0; i < moves_.length(); ++i) {
1695 const MoveOperands& other_move = *moves_[i];
1696 if (other_move.Blocks(destination) && !other_move.IsPending()) {
1697 // Though PerformMove can change any source operand in the move graph,
1698 // this call cannot create a blocking move via a swap (this loop does
1699 // not miss any). Assume there is a non-blocking move with source A
1700 // and this move is blocked on source B and there is a swap of A and
1701 // B. Then A and B must be involved in the same cycle (or they would
1702 // not be swapped). Since this move's destination is B and there is
1703 // only a single incoming edge to an operand, this move must also be
1704 // involved in the same cycle. In that case, the blocking move will
1705 // be created but will be "pending" when we return from PerformMove.
1706 PerformMove(i);
1707 }
1708 }
1709
1710 // We are about to resolve this move and don't need it marked as
1711 // pending, so restore its destination.
1712 moves_[index]->ClearPending(destination);
1713
1714 // This move's source may have changed due to swaps to resolve cycles and
1715 // so it may now be the last move in the cycle. If so remove it.
1716 if (moves_[index]->src().Equals(destination)) {
1717 moves_[index]->Eliminate();
1718 return;
1719 }
1720
1721 // The move may be blocked on a (at most one) pending move, in which case
1722 // we have a cycle. Search for such a blocking move and perform a swap to
1723 // resolve it.
1724 for (int i = 0; i < moves_.length(); ++i) {
1725 const MoveOperands& other_move = *moves_[i];
1726 if (other_move.Blocks(destination)) {
1727 ASSERT(other_move.IsPending());
1728 compiler_->BeginCodeSourceRange();
1729 EmitSwap(index);
1730 compiler_->EndCodeSourceRange(TokenPosition::kParallelMove);
1731 return;
1732 }
1733 }
1734
1735 // This move is not blocked.
1736 compiler_->BeginCodeSourceRange();
1737 EmitMove(index);
1738 compiler_->EndCodeSourceRange(TokenPosition::kParallelMove);
1739}
1740
1741void ParallelMoveResolver::EmitMove(int index) {
1742 MoveOperands* const move = moves_[index];
1743 const Location dst = move->dest();
1744 if (dst.IsStackSlot() || dst.IsDoubleStackSlot()) {
1745 ASSERT((dst.base_reg() != FPREG) ||
1746 ((-compiler::target::frame_layout.VariableIndexForFrameSlot(
1747 dst.stack_index())) < compiler_->StackSize()));
1748 }
1749 const Location src = move->src();
1750 ParallelMoveResolver::TemporaryAllocator temp(this, /*blocked=*/kNoRegister);
1751 compiler_->EmitMove(dst, src, &temp);
1752#if defined(DEBUG)
1753 // Allocating a scratch register here may cause stack spilling. Neither the
1754 // source nor destination register should be SP-relative in that case.
1755 for (const Location& loc : {dst, src}) {
1756 ASSERT(!temp.DidAllocateTemporary() || !loc.HasStackIndex() ||
1757 loc.base_reg() != SPREG);
1758 }
1759#endif
1760 move->Eliminate();
1761}
1762
1763bool ParallelMoveResolver::IsScratchLocation(Location loc) {
1764 for (int i = 0; i < moves_.length(); ++i) {
1765 if (moves_[i]->Blocks(loc)) {
1766 return false;
1767 }
1768 }
1769
1770 for (int i = 0; i < moves_.length(); ++i) {
1771 if (moves_[i]->dest().Equals(loc)) {
1772 return true;
1773 }
1774 }
1775
1776 return false;
1777}
1778
1779intptr_t ParallelMoveResolver::AllocateScratchRegister(
1780 Location::Kind kind,
1781 uword blocked_mask,
1782 intptr_t first_free_register,
1783 intptr_t last_free_register,
1784 bool* spilled) {
1785 COMPILE_ASSERT(static_cast<intptr_t>(sizeof(blocked_mask)) * kBitsPerByte >=
1786 kNumberOfFpuRegisters);
1787 COMPILE_ASSERT(static_cast<intptr_t>(sizeof(blocked_mask)) * kBitsPerByte >=
1788 kNumberOfCpuRegisters);
1789 intptr_t scratch = -1;
1790 for (intptr_t reg = first_free_register; reg <= last_free_register; reg++) {
1791 if ((((1 << reg) & blocked_mask) == 0) &&
1792 IsScratchLocation(Location::MachineRegisterLocation(kind, reg))) {
1793 scratch = reg;
1794 break;
1795 }
1796 }
1797
1798 if (scratch == -1) {
1799 *spilled = true;
1800 for (intptr_t reg = first_free_register; reg <= last_free_register; reg++) {
1801 if (((1 << reg) & blocked_mask) == 0) {
1802 scratch = reg;
1803 break;
1804 }
1805 }
1806 } else {
1807 *spilled = false;
1808 }
1809
1810 return scratch;
1811}
1812
1813ParallelMoveResolver::ScratchFpuRegisterScope::ScratchFpuRegisterScope(
1814 ParallelMoveResolver* resolver,
1815 FpuRegister blocked)
1816 : resolver_(resolver), reg_(kNoFpuRegister), spilled_(false) {
1817 COMPILE_ASSERT(FpuTMP != kNoFpuRegister);
1818 uword blocked_mask =
1819 ((blocked != kNoFpuRegister) ? 1 << blocked : 0) | 1 << FpuTMP;
1820 reg_ = static_cast<FpuRegister>(resolver_->AllocateScratchRegister(
1821 Location::kFpuRegister, blocked_mask, 0, kNumberOfFpuRegisters - 1,
1822 &spilled_));
1823
1824 if (spilled_) {
1825 resolver->SpillFpuScratch(reg_);
1826 }
1827}
1828
1829ParallelMoveResolver::ScratchFpuRegisterScope::~ScratchFpuRegisterScope() {
1830 if (spilled_) {
1831 resolver_->RestoreFpuScratch(reg_);
1832 }
1833}
1834
1835ParallelMoveResolver::TemporaryAllocator::TemporaryAllocator(
1836 ParallelMoveResolver* resolver,
1837 Register blocked)
1838 : resolver_(resolver),
1839 blocked_(blocked),
1840 reg_(kNoRegister),
1841 spilled_(false) {}
1842
1843Register ParallelMoveResolver::TemporaryAllocator::AllocateTemporary() {
1844 ASSERT(reg_ == kNoRegister);
1845
1846 uword blocked_mask = RegMaskBit(blocked_) | kReservedCpuRegisters;
1847 if (resolver_->compiler_->intrinsic_mode()) {
1848 // Block additional registers that must be preserved for intrinsics.
1849 blocked_mask |= RegMaskBit(ARGS_DESC_REG);
1850#if !defined(TARGET_ARCH_IA32)
1851 // Need to preserve CODE_REG to be able to store the PC marker
1852 // and load the pool pointer.
1853 blocked_mask |= RegMaskBit(CODE_REG);
1854#endif
1855 }
1856 reg_ = static_cast<Register>(
1857 resolver_->AllocateScratchRegister(Location::kRegister, blocked_mask, 0,
1858 kNumberOfCpuRegisters - 1, &spilled_));
1859
1860 if (spilled_) {
1861 resolver_->SpillScratch(reg_);
1862 }
1863
1864 DEBUG_ONLY(allocated_ = true;)
1865 return reg_;
1866}
1867
1868void ParallelMoveResolver::TemporaryAllocator::ReleaseTemporary() {
1869 if (spilled_) {
1870 resolver_->RestoreScratch(reg_);
1871 }
1872 reg_ = kNoRegister;
1873}
1874
1875ParallelMoveResolver::ScratchRegisterScope::ScratchRegisterScope(
1876 ParallelMoveResolver* resolver,
1877 Register blocked)
1878 : allocator_(resolver, blocked) {
1879 reg_ = allocator_.AllocateTemporary();
1880}
1881
1882ParallelMoveResolver::ScratchRegisterScope::~ScratchRegisterScope() {
1883 allocator_.ReleaseTemporary();
1884}
1885
1886const ICData* FlowGraphCompiler::GetOrAddInstanceCallICData(
1887 intptr_t deopt_id,
1888 const String& target_name,
1889 const Array& arguments_descriptor,
1890 intptr_t num_args_tested,
1891 const AbstractType& receiver_type) {
1892 if ((deopt_id_to_ic_data_ != NULL) &&
1893 ((*deopt_id_to_ic_data_)[deopt_id] != NULL)) {
1894 const ICData* res = (*deopt_id_to_ic_data_)[deopt_id];
1895 ASSERT(res->deopt_id() == deopt_id);
1896 ASSERT(res->target_name() == target_name.raw());
1897 ASSERT(res->NumArgsTested() == num_args_tested);
1898 ASSERT(res->TypeArgsLen() ==
1899 ArgumentsDescriptor(arguments_descriptor).TypeArgsLen());
1900 ASSERT(!res->is_static_call());
1901 ASSERT(res->receivers_static_type() == receiver_type.raw());
1902 return res;
1903 }
1904 const ICData& ic_data = ICData::ZoneHandle(
1905 zone(), ICData::New(parsed_function().function(), target_name,
1906 arguments_descriptor, deopt_id, num_args_tested,
1907 ICData::kInstance, receiver_type));
1908 if (deopt_id_to_ic_data_ != NULL) {
1909 (*deopt_id_to_ic_data_)[deopt_id] = &ic_data;
1910 }
1911 ASSERT(!ic_data.is_static_call());
1912 return &ic_data;
1913}
1914
1915const ICData* FlowGraphCompiler::GetOrAddStaticCallICData(
1916 intptr_t deopt_id,
1917 const Function& target,
1918 const Array& arguments_descriptor,
1919 intptr_t num_args_tested,
1920 ICData::RebindRule rebind_rule) {
1921 if ((deopt_id_to_ic_data_ != NULL) &&
1922 ((*deopt_id_to_ic_data_)[deopt_id] != NULL)) {
1923 const ICData* res = (*deopt_id_to_ic_data_)[deopt_id];
1924 ASSERT(res->deopt_id() == deopt_id);
1925 ASSERT(res->target_name() == target.name());
1926 ASSERT(res->NumArgsTested() == num_args_tested);
1927 ASSERT(res->TypeArgsLen() ==
1928 ArgumentsDescriptor(arguments_descriptor).TypeArgsLen());
1929 ASSERT(res->is_static_call());
1930 return res;
1931 }
1932
1933 const ICData& ic_data = ICData::ZoneHandle(
1934 zone(),
1935 ICData::New(parsed_function().function(),
1936 String::Handle(zone(), target.name()), arguments_descriptor,
1937 deopt_id, num_args_tested, rebind_rule));
1938 ic_data.AddTarget(target);
1939 if (deopt_id_to_ic_data_ != NULL) {
1940 (*deopt_id_to_ic_data_)[deopt_id] = &ic_data;
1941 }
1942 return &ic_data;
1943}
1944
1945intptr_t FlowGraphCompiler::GetOptimizationThreshold() const {
1946 intptr_t threshold;
1947 if (is_optimizing()) {
1948 threshold = FLAG_reoptimization_counter_threshold;
1949 } else if (parsed_function_.function().IsIrregexpFunction()) {
1950 threshold = FLAG_regexp_optimization_counter_threshold;
1951 } else if (FLAG_randomize_optimization_counter) {
1952 threshold = Thread::Current()->GetRandomUInt64() %
1953 FLAG_optimization_counter_threshold;
1954 } else {
1955 const intptr_t basic_blocks = flow_graph().preorder().length();
1956 ASSERT(basic_blocks > 0);
1957 threshold = FLAG_optimization_counter_scale * basic_blocks +
1958 FLAG_min_optimization_counter_threshold;
1959 if (threshold > FLAG_optimization_counter_threshold) {
1960 threshold = FLAG_optimization_counter_threshold;
1961 }
1962 }
1963
1964 // Threshold = 0 doesn't make sense because we increment the counter before
1965 // testing against the threshold. Perhaps we could interpret it to mean
1966 // "generate optimized code immediately without unoptimized compilation
1967 // first", but this isn't supported in our pipeline because there would be no
1968 // code for the optimized code to deoptimize into.
1969 if (threshold == 0) threshold = 1;
1970
1971 // See Compiler::CanOptimizeFunction. In short, we have to allow the
1972 // unoptimized code to run at least once to prevent an infinite compilation
1973 // loop.
1974 if (threshold == 1 && parsed_function().function().HasBreakpoint()) {
1975 threshold = 2;
1976 }
1977
1978 return threshold;
1979}
1980
1981const Class& FlowGraphCompiler::BoxClassFor(Representation rep) {
1982 switch (rep) {
1983 case kUnboxedFloat:
1984 case kUnboxedDouble:
1985 return double_class();
1986 case kUnboxedFloat32x4:
1987 return float32x4_class();
1988 case kUnboxedFloat64x2:
1989 return float64x2_class();
1990 case kUnboxedInt32x4:
1991 return int32x4_class();
1992 case kUnboxedInt64:
1993 return mint_class();
1994 default:
1995 UNREACHABLE();
1996 return Class::ZoneHandle();
1997 }
1998}
1999
2000void FlowGraphCompiler::BeginCodeSourceRange() {
2001 code_source_map_builder_->BeginCodeSourceRange(assembler()->CodeSize());
2002}
2003
2004void FlowGraphCompiler::EndCodeSourceRange(TokenPosition token_pos) {
2005 code_source_map_builder_->EndCodeSourceRange(assembler()->CodeSize(),
2006 token_pos);
2007}
2008
2009const CallTargets* FlowGraphCompiler::ResolveCallTargetsForReceiverCid(
2010 intptr_t cid,
2011 const String& selector,
2012 const Array& args_desc_array) {
2013 Zone* zone = Thread::Current()->zone();
2014
2015 ArgumentsDescriptor args_desc(args_desc_array);
2016
2017 Function& fn = Function::ZoneHandle(zone);
2018 if (!LookupMethodFor(cid, selector, args_desc, &fn)) return NULL;
2019
2020 CallTargets* targets = new (zone) CallTargets(zone);
2021 targets->Add(new (zone) TargetInfo(cid, cid, &fn, /* count = */ 1,
2022 StaticTypeExactnessState::NotTracking()));
2023
2024 return targets;
2025}
2026
2027bool FlowGraphCompiler::LookupMethodFor(int class_id,
2028 const String& name,
2029 const ArgumentsDescriptor& args_desc,
2030 Function* fn_return,
2031 bool* class_is_abstract_return) {
2032 Thread* thread = Thread::Current();
2033 Isolate* isolate = thread->isolate();
2034 Zone* zone = thread->zone();
2035 if (class_id < 0) return false;
2036 if (class_id >= isolate->class_table()->NumCids()) return false;
2037
2038 ClassPtr raw_class = isolate->class_table()->At(class_id);
2039 if (raw_class == nullptr) return false;
2040 Class& cls = Class::Handle(zone, raw_class);
2041 if (cls.IsNull()) return false;
2042 if (!cls.is_finalized()) return false;
2043 if (Array::Handle(cls.functions()).IsNull()) return false;
2044
2045 if (class_is_abstract_return != NULL) {
2046 *class_is_abstract_return = cls.is_abstract();
2047 }
2048 const bool allow_add = false;
2049 Function& target_function =
2050 Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
2051 cls, name, args_desc, allow_add));
2052 if (target_function.IsNull()) return false;
2053 *fn_return = target_function.raw();
2054 return true;
2055}
2056
2057void FlowGraphCompiler::EmitPolymorphicInstanceCall(
2058 const PolymorphicInstanceCallInstr* call,
2059 const CallTargets& targets,
2060 ArgumentsInfo args_info,
2061 intptr_t deopt_id,
2062 TokenPosition token_pos,
2063 LocationSummary* locs,
2064 bool complete,
2065 intptr_t total_ic_calls,
2066 bool receiver_can_be_smi) {
2067 ASSERT(call != nullptr);
2068 if (FLAG_polymorphic_with_deopt) {
2069 compiler::Label* deopt =
2070 AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail);
2071 compiler::Label ok;
2072 EmitTestAndCall(targets, call->function_name(), args_info,
2073 deopt, // No cid match.
2074 &ok, // Found cid.
2075 deopt_id, token_pos, locs, complete, total_ic_calls,
2076 call->entry_kind());
2077 assembler()->Bind(&ok);
2078 } else {
2079 if (complete) {
2080 compiler::Label ok;
2081 EmitTestAndCall(targets, call->function_name(), args_info,
2082 NULL, // No cid match.
2083 &ok, // Found cid.
2084 deopt_id, token_pos, locs, true, total_ic_calls,
2085 call->entry_kind());
2086 assembler()->Bind(&ok);
2087 } else {
2088 const ICData& unary_checks =
2089 ICData::ZoneHandle(zone(), call->ic_data()->AsUnaryClassChecks());
2090 EmitInstanceCallAOT(unary_checks, deopt_id, token_pos, locs,
2091 call->entry_kind(), receiver_can_be_smi);
2092 }
2093 }
2094}
2095
2096#define __ assembler()->
2097void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
2098 const String& function_name,
2099 ArgumentsInfo args_info,
2100 compiler::Label* failed,
2101 compiler::Label* match_found,
2102 intptr_t deopt_id,
2103 TokenPosition token_index,
2104 LocationSummary* locs,
2105 bool complete,
2106 intptr_t total_ic_calls,
2107 Code::EntryKind entry_kind) {
2108 ASSERT(is_optimizing());
2109 ASSERT(complete || (failed != nullptr)); // Complete calls can't fail.
2110
2111 const Array& arguments_descriptor =
2112 Array::ZoneHandle(zone(), args_info.ToArgumentsDescriptor());
2113 EmitTestAndCallLoadReceiver(args_info.count_without_type_args,
2114 arguments_descriptor);
2115
2116 static const int kNoCase = -1;
2117 int smi_case = kNoCase;
2118 int which_case_to_skip = kNoCase;
2119
2120 const int length = targets.length();
2121 ASSERT(length > 0);
2122 int non_smi_length = length;
2123
2124 // Find out if one of the classes in one of the cases is the Smi class. We
2125 // will be handling that specially.
2126 for (int i = 0; i < length; i++) {
2127 const intptr_t start = targets[i].cid_start;
2128 if (start > kSmiCid) continue;
2129 const intptr_t end = targets[i].cid_end;
2130 if (end >= kSmiCid) {
2131 smi_case = i;
2132 if (start == kSmiCid && end == kSmiCid) {
2133 // If this case has only the Smi class then we won't need to emit it at
2134 // all later.
2135 which_case_to_skip = i;
2136 non_smi_length--;
2137 }
2138 break;
2139 }
2140 }
2141
2142 if (smi_case != kNoCase) {
2143 compiler::Label after_smi_test;
2144 // If the call is complete and there are no other possible receiver
2145 // classes - then receiver can only be a smi value and we don't need
2146 // to check if it is a smi.
2147 if (!(complete && non_smi_length == 0)) {
2148 EmitTestAndCallSmiBranch(non_smi_length == 0 ? failed : &after_smi_test,
2149 /* jump_if_smi= */ false);
2150 }
2151
2152 // Do not use the code from the function, but let the code be patched so
2153 // that we can record the outgoing edges to other code.
2154 const Function& function = *targets.TargetAt(smi_case)->target;
2155 GenerateStaticDartCall(deopt_id, token_index, PcDescriptorsLayout::kOther,
2156 locs, function, entry_kind);
2157 __ Drop(args_info.size_with_type_args);
2158 if (match_found != NULL) {
2159 __ Jump(match_found);
2160 }
2161 __ Bind(&after_smi_test);
2162 } else {
2163 if (!complete) {
2164 // Smi is not a valid class.
2165 EmitTestAndCallSmiBranch(failed, /* jump_if_smi = */ true);
2166 }
2167 }
2168
2169 if (non_smi_length == 0) {
2170 // If non_smi_length is 0 then only a Smi check was needed; the Smi check
2171 // above will fail if there was only one check and receiver is not Smi.
2172 return;
2173 }
2174
2175 bool add_megamorphic_call = false;
2176 int bias = 0;
2177
2178 // Value is not Smi.
2179 EmitTestAndCallLoadCid(EmitTestCidRegister());
2180
2181 int last_check = which_case_to_skip == length - 1 ? length - 2 : length - 1;
2182
2183 for (intptr_t i = 0; i < length; i++) {
2184 if (i == which_case_to_skip) continue;
2185 const bool is_last_check = (i == last_check);
2186 const int count = targets.TargetAt(i)->count;
2187 if (!is_last_check && !complete && count < (total_ic_calls >> 5)) {
2188 // This case is hit too rarely to be worth writing class-id checks inline
2189 // for. Note that we can't do this for calls with only one target because
2190 // the type propagator may have made use of that and expects a deopt if
2191 // a new class is seen at this calls site. See IsMonomorphic.
2192 add_megamorphic_call = true;
2193 break;
2194 }
2195 compiler::Label next_test;
2196 if (!complete || !is_last_check) {
2197 bias = EmitTestAndCallCheckCid(assembler(),
2198 is_last_check ? failed : &next_test,
2199 EmitTestCidRegister(), targets[i], bias,
2200 /*jump_on_miss =*/true);
2201 }
2202 // Do not use the code from the function, but let the code be patched so
2203 // that we can record the outgoing edges to other code.
2204 const Function& function = *targets.TargetAt(i)->target;
2205 GenerateStaticDartCall(deopt_id, token_index, PcDescriptorsLayout::kOther,
2206 locs, function, entry_kind);
2207 __ Drop(args_info.size_with_type_args);
2208 if (!is_last_check || add_megamorphic_call) {
2209 __ Jump(match_found);
2210 }
2211 __ Bind(&next_test);
2212 }
2213 if (add_megamorphic_call) {
2214 int try_index = kInvalidTryIndex;
2215 EmitMegamorphicInstanceCall(function_name, arguments_descriptor, deopt_id,
2216 token_index, locs, try_index);
2217 }
2218}
2219
2220bool FlowGraphCompiler::GenerateSubtypeRangeCheck(Register class_id_reg,
2221 const Class& type_class,
2222 compiler::Label* is_subtype) {
2223 HierarchyInfo* hi = Thread::Current()->hierarchy_info();
2224 if (hi != NULL) {
2225 const CidRangeVector& ranges =
2226 hi->SubtypeRangesForClass(type_class,
2227 /*include_abstract=*/false,
2228 /*exclude_null=*/false);
2229 if (ranges.length() <= kMaxNumberOfCidRangesToTest) {
2230 GenerateCidRangesCheck(assembler(), class_id_reg, ranges, is_subtype);
2231 return true;
2232 }
2233 }
2234
2235 // We don't have cid-ranges for subclasses, so we'll just test against the
2236 // class directly if it's non-abstract.
2237 if (!type_class.is_abstract()) {
2238 __ CompareImmediate(class_id_reg, type_class.id());
2239 __ BranchIf(EQUAL, is_subtype);
2240 }
2241 return false;
2242}
2243
2244void FlowGraphCompiler::GenerateCidRangesCheck(
2245 compiler::Assembler* assembler,
2246 Register class_id_reg,
2247 const CidRangeVector& cid_ranges,
2248 compiler::Label* inside_range_lbl,
2249 compiler::Label* outside_range_lbl,
2250 bool fall_through_if_inside) {
2251 // If there are no valid class ranges, the check will fail. If we are
2252 // supposed to fall-through in the positive case, we'll explicitly jump to
2253 // the [outside_range_lbl].
2254 if (cid_ranges.length() == 1 && cid_ranges[0].IsIllegalRange()) {
2255 if (fall_through_if_inside) {
2256 assembler->Jump(outside_range_lbl);
2257 }
2258 return;
2259 }
2260
2261 int bias = 0;
2262 for (intptr_t i = 0; i < cid_ranges.length(); ++i) {
2263 const CidRangeValue& range = cid_ranges[i];
2264 RELEASE_ASSERT(!range.IsIllegalRange());
2265 const bool last_round = i == (cid_ranges.length() - 1);
2266
2267 compiler::Label* jump_label = last_round && fall_through_if_inside
2268 ? outside_range_lbl
2269 : inside_range_lbl;
2270 const bool jump_on_miss = last_round && fall_through_if_inside;
2271
2272 bias = EmitTestAndCallCheckCid(assembler, jump_label, class_id_reg, range,
2273 bias, jump_on_miss);
2274 }
2275}
2276
2277bool FlowGraphCompiler::CheckAssertAssignableTypeTestingABILocations(
2278 const LocationSummary& locs) {
2279 ASSERT(locs.in(0).IsRegister() &&
2280 locs.in(0).reg() == TypeTestABI::kInstanceReg);
2281 ASSERT((locs.in(1).IsConstant() && locs.in(1).constant().IsAbstractType()) ||
2282 (locs.in(1).IsRegister() &&
2283 locs.in(1).reg() == TypeTestABI::kDstTypeReg));
2284 ASSERT(locs.in(2).IsRegister() &&
2285 locs.in(2).reg() == TypeTestABI::kInstantiatorTypeArgumentsReg);
2286 ASSERT(locs.in(3).IsRegister() &&
2287 locs.in(3).reg() == TypeTestABI::kFunctionTypeArgumentsReg);
2288 ASSERT(locs.out(0).IsRegister() &&
2289 locs.out(0).reg() == TypeTestABI::kInstanceReg);
2290 return true;
2291}
2292
2293bool FlowGraphCompiler::ShouldUseTypeTestingStubFor(bool optimizing,
2294 const AbstractType& type) {
2295 return FLAG_precompiled_mode ||
2296 (optimizing &&
2297 (type.IsTypeParameter() || (type.IsType() && type.IsInstantiated())));
2298}
2299
2300FlowGraphCompiler::TypeTestStubKind
2301FlowGraphCompiler::GetTypeTestStubKindForTypeParameter(
2302 const TypeParameter& type_param) {
2303 // If it's guaranteed, by type-parameter bound, that the type parameter will
2304 // never have a value of a function type, then we can safely do a 4-type
2305 // test instead of a 6-type test.
2306 AbstractType& bound = AbstractType::Handle(zone(), type_param.bound());
2307 bound = bound.UnwrapFutureOr();
2308 return !bound.IsTopTypeForSubtyping() && !bound.IsObjectType() &&
2309 !bound.IsFunctionType() && !bound.IsDartFunctionType() &&
2310 bound.IsType()
2311 ? kTestTypeFourArgs
2312 : kTestTypeSixArgs;
2313}
2314
2315void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
2316 CompileType* receiver_type,
2317 const AbstractType& dst_type,
2318 const String& dst_name,
2319 const Register dst_type_reg_to_call,
2320 const Register scratch_reg,
2321 compiler::Label* done) {
2322#if defined(TARGET_ARCH_IA32)
2323 // ia32 does not have support for TypeTestingStubs.
2324 UNREACHABLE();
2325#else
2326 TypeUsageInfo* type_usage_info = thread()->type_usage_info();
2327
2328 // Special case: non-nullable Object.
2329 // Top types should be handled by the caller and cannot reach here.
2330 ASSERT(!dst_type.IsTopTypeForSubtyping());
2331 if (dst_type.IsObjectType()) {
2332 ASSERT(dst_type.IsNonNullable() && isolate()->null_safety());
2333 __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
2334 __ BranchIf(NOT_EQUAL, done);
2335 // Fall back to type testing stub.
2336 __ LoadObject(TypeTestABI::kDstTypeReg, dst_type);
2337 return;
2338 }
2339
2340 // If the int type is assignable to [dst_type] we special case it on the
2341 // caller side!
2342 const Type& int_type = Type::Handle(zone(), Type::IntType());
2343 bool is_non_smi = false;
2344 if (int_type.IsSubtypeOf(dst_type, Heap::kOld)) {
2345 __ BranchIfSmi(TypeTestABI::kInstanceReg, done);
2346 is_non_smi = true;
2347 } else if (!receiver_type->CanBeSmi()) {
2348 is_non_smi = true;
2349 }
2350
2351 // We use two type registers iff the dst type is a type parameter.
2352 // We "dereference" the type parameter for the TTS call but leave the type
2353 // parameter in the TypeTestABI::kDstTypeReg for fallback into
2354 // SubtypeTestCache.
2355 ASSERT(dst_type_reg_to_call == kNoRegister ||
2356 (dst_type.IsTypeParameter() ==
2357 (TypeTestABI::kDstTypeReg != dst_type_reg_to_call)));
2358
2359 // We can handle certain types very efficiently on the call site (with a
2360 // bailout to the normal stub, which will do a runtime call).
2361 if (dst_type.IsTypeParameter()) {
2362 // In NNBD strong mode we need to handle null instance before calling TTS
2363 // if type parameter is nullable or legacy because type parameter can be
2364 // instantiated with a non-nullable type which rejects null.
2365 // In NNBD weak mode or if type parameter is non-nullable or has
2366 // undetermined nullability null instance is correctly handled by TTS.
2367 if (isolate()->null_safety() &&
2368 (dst_type.IsNullable() || dst_type.IsLegacy())) {
2369 __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
2370 __ BranchIf(EQUAL, done);
2371 }
2372 const TypeParameter& type_param = TypeParameter::Cast(dst_type);
2373 const Register kTypeArgumentsReg =
2374 type_param.IsClassTypeParameter()
2375 ? TypeTestABI::kInstantiatorTypeArgumentsReg
2376 : TypeTestABI::kFunctionTypeArgumentsReg;
2377
2378 // Check if type arguments are null, i.e. equivalent to vector of dynamic.
2379 __ CompareObject(kTypeArgumentsReg, Object::null_object());
2380 __ BranchIf(EQUAL, done);
2381 __ LoadField(
2382 dst_type_reg_to_call,
2383 compiler::FieldAddress(kTypeArgumentsReg,
2384 compiler::target::TypeArguments::type_at_offset(
2385 type_param.index())));
2386 __ LoadObject(TypeTestABI::kDstTypeReg, type_param);
2387 if (type_usage_info != NULL) {
2388 type_usage_info->UseTypeInAssertAssignable(dst_type);
2389 }
2390 } else {
2391 HierarchyInfo* hi = Thread::Current()->hierarchy_info();
2392 if (hi != NULL) {
2393 const Class& type_class = Class::Handle(zone(), dst_type.type_class());
2394
2395 bool check_handled_at_callsite = false;
2396 bool used_cid_range_check = false;
2397 const bool can_use_simple_cid_range_test =
2398 hi->CanUseSubtypeRangeCheckFor(dst_type);
2399 if (can_use_simple_cid_range_test) {
2400 const CidRangeVector& ranges = hi->SubtypeRangesForClass(
2401 type_class,
2402 /*include_abstract=*/false,
2403 /*exclude_null=*/!Instance::NullIsAssignableTo(dst_type));
2404 if (ranges.length() <= kMaxNumberOfCidRangesToTest) {
2405 if (is_non_smi) {
2406 __ LoadClassId(scratch_reg, TypeTestABI::kInstanceReg);
2407 } else {
2408 __ LoadClassIdMayBeSmi(scratch_reg, TypeTestABI::kInstanceReg);
2409 }
2410 GenerateCidRangesCheck(assembler(), scratch_reg, ranges, done);
2411 used_cid_range_check = true;
2412 check_handled_at_callsite = true;
2413 }
2414 }
2415
2416 if (!used_cid_range_check && can_use_simple_cid_range_test &&
2417 IsListClass(type_class)) {
2418 __ LoadClassIdMayBeSmi(scratch_reg, TypeTestABI::kInstanceReg);
2419 GenerateListTypeCheck(scratch_reg, done);
2420 used_cid_range_check = true;
2421 }
2422
2423 // If we haven't handled the positive case of the type check on the
2424 // call-site, we want an optimized type testing stub and therefore record
2425 // it in the [TypeUsageInfo].
2426 if (!check_handled_at_callsite) {
2427 if (type_usage_info != NULL) {
2428 type_usage_info->UseTypeInAssertAssignable(dst_type);
2429 } else {
2430 ASSERT(!FLAG_precompiled_mode);
2431 }
2432 }
2433 }
2434 __ LoadObject(TypeTestABI::kDstTypeReg, dst_type);
2435 }
2436#endif // defined(TARGET_ARCH_IA32)
2437}
2438
2439#undef __
2440
2441#if defined(DEBUG)
2442void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) {
2443 ASSERT(!is_optimizing());
2444
2445 switch (instr->tag()) {
2446 case Instruction::kPushArgument:
2447 // Do nothing.
2448 break;
2449
2450 case Instruction::kDropTemps:
2451 FrameStatePop(instr->locs()->input_count() +
2452 instr->AsDropTemps()->num_temps());
2453 break;
2454
2455 default:
2456 FrameStatePop(instr->locs()->input_count());
2457 break;
2458 }
2459
2460 ASSERT(!instr->locs()->can_call() || FrameStateIsSafeToCall());
2461
2462 FrameStatePop(instr->ArgumentCount());
2463 Definition* defn = instr->AsDefinition();
2464 if ((defn != NULL) && defn->HasTemp()) {
2465 FrameStatePush(defn);
2466 }
2467}
2468
2469void FlowGraphCompiler::FrameStatePush(Definition* defn) {
2470 Representation rep = defn->representation();
2471 if ((rep == kUnboxedDouble) || (rep == kUnboxedFloat64x2) ||
2472 (rep == kUnboxedFloat32x4)) {
2473 // LoadField instruction lies about its representation in the unoptimized
2474 // code because Definition::representation() can't depend on the type of
2475 // compilation but MakeLocationSummary and EmitNativeCode can.
2476 ASSERT(defn->IsLoadField() && defn->AsLoadField()->IsUnboxedLoad());
2477 ASSERT(defn->locs()->out(0).IsRegister());
2478 rep = kTagged;
2479 }
2480 ASSERT(!is_optimizing());
2481 ASSERT((rep == kTagged) || (rep == kUntagged));
2482 ASSERT(rep != kUntagged || flow_graph_.IsIrregexpFunction());
2483 frame_state_.Add(rep);
2484}
2485
2486void FlowGraphCompiler::FrameStatePop(intptr_t count) {
2487 ASSERT(!is_optimizing());
2488 frame_state_.TruncateTo(
2489 Utils::Maximum(static_cast<intptr_t>(0), frame_state_.length() - count));
2490}
2491
2492bool FlowGraphCompiler::FrameStateIsSafeToCall() {
2493 ASSERT(!is_optimizing());
2494 for (intptr_t i = 0; i < frame_state_.length(); i++) {
2495 if (frame_state_[i] != kTagged) {
2496 return false;
2497 }
2498 }
2499 return true;
2500}
2501
2502void FlowGraphCompiler::FrameStateClear() {
2503 ASSERT(!is_optimizing());
2504 frame_state_.TruncateTo(0);
2505}
2506#endif // defined(DEBUG)
2507
2508#define __ compiler->assembler()->
2509
2510void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
2511 if (compiler::Assembler::EmittingComments()) {
2512 __ Comment("slow path %s operation", name());
2513 }
2514 const bool use_shared_stub =
2515 instruction()->UseSharedSlowPathStub(compiler->is_optimizing());
2516 const bool live_fpu_registers =
2517 instruction()->locs()->live_registers()->FpuRegisterCount() > 0;
2518 __ Bind(entry_label());
2519 EmitCodeAtSlowPathEntry(compiler);
2520 LocationSummary* locs = instruction()->locs();
2521 // Save registers as they are needed for lazy deopt / exception handling.
2522 if (use_shared_stub) {
2523 EmitSharedStubCall(compiler, live_fpu_registers);
2524 } else {
2525 compiler->SaveLiveRegisters(locs);
2526 intptr_t i = 0;
2527 if (num_args_ % 2 != 0) {
2528 __ PushRegister(locs->in(i).reg());
2529 ++i;
2530 }
2531 for (; i < num_args_; i += 2) {
2532 __ PushRegisterPair(locs->in(i + 1).reg(), locs->in(i).reg());
2533 }
2534 __ CallRuntime(runtime_entry_, num_args_);
2535 }
2536 const intptr_t deopt_id = instruction()->deopt_id();
2537 compiler->AddDescriptor(PcDescriptorsLayout::kOther,
2538 compiler->assembler()->CodeSize(), deopt_id,
2539 instruction()->token_pos(), try_index_);
2540 AddMetadataForRuntimeCall(compiler);
2541 compiler->RecordSafepoint(locs, num_args_);
2542 if ((try_index_ != kInvalidTryIndex) ||
2543 (compiler->CurrentTryIndex() != kInvalidTryIndex)) {
2544 Environment* env =
2545 compiler->SlowPathEnvironmentFor(instruction(), num_args_);
2546 if (FLAG_precompiled_mode) {
2547 compiler->RecordCatchEntryMoves(env, try_index_);
2548 } else if (env != nullptr) {
2549 compiler->AddSlowPathDeoptInfo(deopt_id, env);
2550 }
2551 }
2552 if (!use_shared_stub) {
2553 __ Breakpoint();
2554 }
2555}
2556
2557const char* NullErrorSlowPath::name() {
2558 switch (exception_type()) {
2559 case CheckNullInstr::kNoSuchMethod:
2560 return "check null (nsm)";
2561 case CheckNullInstr::kArgumentError:
2562 return "check null (arg)";
2563 case CheckNullInstr::kCastError:
2564 return "check null (cast)";
2565 }
2566 UNREACHABLE();
2567}
2568
2569const RuntimeEntry& NullErrorSlowPath::GetRuntimeEntry(
2570 CheckNullInstr::ExceptionType exception_type) {
2571 switch (exception_type) {
2572 case CheckNullInstr::kNoSuchMethod:
2573 return kNullErrorRuntimeEntry;
2574 case CheckNullInstr::kArgumentError:
2575 return kArgumentNullErrorRuntimeEntry;
2576 case CheckNullInstr::kCastError:
2577 return kNullCastErrorRuntimeEntry;
2578 }
2579 UNREACHABLE();
2580}
2581
2582CodePtr NullErrorSlowPath::GetStub(FlowGraphCompiler* compiler,
2583 CheckNullInstr::ExceptionType exception_type,
2584 bool save_fpu_registers) {
2585 auto object_store = compiler->isolate()->object_store();
2586 switch (exception_type) {
2587 case CheckNullInstr::kNoSuchMethod:
2588 return save_fpu_registers
2589 ? object_store->null_error_stub_with_fpu_regs_stub()
2590 : object_store->null_error_stub_without_fpu_regs_stub();
2591 case CheckNullInstr::kArgumentError:
2592 return save_fpu_registers
2593 ? object_store->null_arg_error_stub_with_fpu_regs_stub()
2594 : object_store->null_arg_error_stub_without_fpu_regs_stub();
2595 case CheckNullInstr::kCastError:
2596 return save_fpu_registers
2597 ? object_store->null_cast_error_stub_with_fpu_regs_stub()
2598 : object_store->null_cast_error_stub_without_fpu_regs_stub();
2599 }
2600 UNREACHABLE();
2601}
2602
2603void NullErrorSlowPath::EmitSharedStubCall(FlowGraphCompiler* compiler,
2604 bool save_fpu_registers) {
2605#if defined(TARGET_ARCH_IA32)
2606 UNREACHABLE();
2607#else
2608 const auto& stub =
2609 Code::ZoneHandle(compiler->zone(),
2610 GetStub(compiler, exception_type(), save_fpu_registers));
2611 compiler->EmitCallToStub(stub);
2612#endif
2613}
2614
2615void FlowGraphCompiler::EmitNativeMove(
2616 const compiler::ffi::NativeLocation& destination,
2617 const compiler::ffi::NativeLocation& source,
2618 TemporaryRegisterAllocator* temp) {
2619 const auto& src_payload_type = source.payload_type();
2620 const auto& dst_payload_type = destination.payload_type();
2621 const auto& src_container_type = source.container_type();
2622 const auto& dst_container_type = destination.container_type();
2623 const intptr_t src_payload_size = src_payload_type.SizeInBytes();
2624 const intptr_t dst_payload_size = dst_payload_type.SizeInBytes();
2625 const intptr_t src_container_size = src_container_type.SizeInBytes();
2626 const intptr_t dst_container_size = dst_container_type.SizeInBytes();
2627
2628 // This function does not know how to do larger mem copy moves yet.
2629 ASSERT(src_payload_type.IsFundamental());
2630 ASSERT(dst_payload_type.IsFundamental());
2631
2632 // This function does not deal with sign conversions yet.
2633 ASSERT(src_payload_type.IsSigned() == dst_payload_type.IsSigned());
2634
2635 // This function does not deal with bit casts yet.
2636 ASSERT(src_container_type.IsFloat() == dst_container_type.IsFloat());
2637 ASSERT(src_container_type.IsInt() == dst_container_type.IsInt());
2638
2639 // If the location, payload, and container are equal, we're done.
2640 if (source.Equals(destination) && src_payload_type.Equals(dst_payload_type) &&
2641 src_container_type.Equals(dst_container_type)) {
2642 return;
2643 }
2644
2645 // Solve descrepancies between container size and payload size.
2646 if (src_payload_type.IsInt() && dst_payload_type.IsInt() &&
2647 (src_payload_size != src_container_size ||
2648 dst_payload_size != dst_container_size)) {
2649 if (src_payload_size <= dst_payload_size &&
2650 src_container_size >= dst_container_size) {
2651 // The upper bits of the source are already properly sign or zero
2652 // extended, so just copy the required amount of bits.
2653 return EmitNativeMove(destination.WithOtherNativeType(
2654 dst_container_type, dst_container_type, zone_),
2655 source.WithOtherNativeType(
2656 dst_container_type, dst_container_type, zone_),
2657 temp);
2658 }
2659 if (src_payload_size >= dst_payload_size &&
2660 dst_container_size > dst_payload_size) {
2661 // The upper bits of the source are not properly sign or zero extended
2662 // to be copied to the target, so regard the source as smaller.
2663 return EmitNativeMove(
2664 destination.WithOtherNativeType(dst_container_type,
2665 dst_container_type, zone_),
2666 source.WithOtherNativeType(dst_payload_type, dst_payload_type, zone_),
2667 temp);
2668 }
2669 UNREACHABLE();
2670 }
2671 ASSERT(src_payload_size == src_container_size);
2672 ASSERT(dst_payload_size == dst_container_size);
2673
2674 // Split moves that are larger than kWordSize, these require separate
2675 // instructions on all architectures.
2676 if (compiler::target::kWordSize == 4 && src_container_size == 8 &&
2677 dst_container_size == 8 && !source.IsFpuRegisters() &&
2678 !destination.IsFpuRegisters()) {
2679 // TODO(40209): If this is stack to stack, we could use FpuTMP.
2680 // Test the impact on code size and speed.
2681 EmitNativeMove(destination.Split(0, zone_), source.Split(0, zone_), temp);
2682 EmitNativeMove(destination.Split(1, zone_), source.Split(1, zone_), temp);
2683 return;
2684 }
2685
2686 // Split moves from stack to stack, none of the architectures provides
2687 // memory to memory move instructions.
2688 if (source.IsStack() && destination.IsStack()) {
2689 Register scratch = TMP;
2690 if (TMP == kNoRegister) {
2691 scratch = temp->AllocateTemporary();
2692 }
2693 const auto& intermediate =
2694 *new (zone_) compiler::ffi::NativeRegistersLocation(
2695 dst_payload_type, dst_container_type, scratch);
2696 EmitNativeMove(intermediate, source, temp);
2697 EmitNativeMove(destination, intermediate, temp);
2698 if (TMP == kNoRegister) {
2699 temp->ReleaseTemporary();
2700 }
2701 return;
2702 }
2703
2704 const bool sign_or_zero_extend = dst_container_size > src_container_size;
2705
2706 // No architecture supports sign extending with memory as destination.
2707 if (sign_or_zero_extend && destination.IsStack()) {
2708 ASSERT(source.IsRegisters());
2709 const auto& intermediate =
2710 source.WithOtherNativeType(dst_payload_type, dst_container_type, zone_);
2711 EmitNativeMove(intermediate, source, temp);
2712 EmitNativeMove(destination, intermediate, temp);
2713 return;
2714 }
2715
2716#if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64)
2717 // Arm does not support sign extending from a memory location, x86 does.
2718 if (sign_or_zero_extend && source.IsStack()) {
2719 ASSERT(destination.IsRegisters());
2720 const auto& intermediate = destination.WithOtherNativeType(
2721 src_payload_type, src_container_type, zone_);
2722 EmitNativeMove(intermediate, source, temp);
2723 EmitNativeMove(destination, intermediate, temp);
2724 return;
2725 }
2726#endif
2727
2728 // If we're not sign extending, and we're moving 8 or 16 bits into a
2729 // register, upgrade the move to take upper bits of garbage from the
2730 // source location. This is the same as leaving the previous garbage in
2731 // there.
2732 //
2733 // TODO(40210): If our assemblers would support moving 1 and 2 bytes into
2734 // registers, this code can be removed.
2735 if (!sign_or_zero_extend && destination.IsRegisters() &&
2736 destination.container_type().SizeInBytes() <= 2) {
2737 ASSERT(source.payload_type().IsInt());
2738 return EmitNativeMove(destination.WidenTo4Bytes(zone_),
2739 source.WidenTo4Bytes(zone_), temp);
2740 }
2741
2742 // Do the simple architecture specific moves.
2743 EmitNativeMoveArchitecture(destination, source);
2744}
2745
2746// TODO(dartbug.com/36730): Remove this if PairLocations can be converted
2747// into NativeLocations.
2748void FlowGraphCompiler::EmitMoveToNative(
2749 const compiler::ffi::NativeLocation& dst,
2750 Location src_loc,
2751 Representation src_type,
2752 TemporaryRegisterAllocator* temp) {
2753 if (src_loc.IsPairLocation()) {
2754 for (intptr_t i : {0, 1}) {
2755 const auto& src_split = compiler::ffi::NativeLocation::FromPairLocation(
2756 src_loc, src_type, i, zone_);
2757 EmitNativeMove(dst.Split(i, zone_), src_split, temp);
2758 }
2759 } else {
2760 const auto& src =
2761 compiler::ffi::NativeLocation::FromLocation(src_loc, src_type, zone_);
2762 EmitNativeMove(dst, src, temp);
2763 }
2764}
2765
2766// TODO(dartbug.com/36730): Remove this if PairLocations can be converted
2767// into NativeLocations.
2768void FlowGraphCompiler::EmitMoveFromNative(
2769 Location dst_loc,
2770 Representation dst_type,
2771 const compiler::ffi::NativeLocation& src,
2772 TemporaryRegisterAllocator* temp) {
2773 if (dst_loc.IsPairLocation()) {
2774 for (intptr_t i : {0, 1}) {
2775 const auto& dest_split = compiler::ffi::NativeLocation::FromPairLocation(
2776 dst_loc, dst_type, i, zone_);
2777 EmitNativeMove(dest_split, src.Split(i, zone_), temp);
2778 }
2779 } else {
2780 const auto& dest =
2781 compiler::ffi::NativeLocation::FromLocation(dst_loc, dst_type, zone_);
2782 EmitNativeMove(dest, src, temp);
2783 }
2784}
2785
2786void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst,
2787 Location src,
2788 Representation src_type,
2789 TemporaryRegisterAllocator* temp) {
2790 ASSERT(src.IsConstant());
2791 const auto& dst_type = dst.payload_type();
2792 if (dst.IsExpressibleAsLocation() &&
2793 dst_type.IsExpressibleAsRepresentation() &&
2794 dst_type.AsRepresentationOverApprox(zone_) == src_type) {
2795 // We can directly emit the const in the right place and representation.
2796 const Location dst_loc = dst.AsLocation();
2797 EmitMove(dst_loc, src, temp);
2798 } else {
2799 // We need an intermediate location.
2800 Location intermediate;
2801 if (dst_type.IsInt()) {
2802 if (TMP == kNoRegister) {
2803 Register scratch = temp->AllocateTemporary();
2804 Location::RegisterLocation(scratch);
2805 } else {
2806 intermediate = Location::RegisterLocation(TMP);
2807 }
2808 } else {
2809 ASSERT(dst_type.IsFloat());
2810 intermediate = Location::FpuRegisterLocation(FpuTMP);
2811 }
2812
2813 if (src.IsPairLocation()) {
2814 for (intptr_t i : {0, 1}) {
2815 const Representation src_type_split =
2816 compiler::ffi::NativeType::FromUnboxedRepresentation(src_type,
2817 zone_)
2818 .Split(i, zone_)
2819 .AsRepresentation();
2820 const auto& intermediate_native =
2821 compiler::ffi::NativeLocation::FromLocation(intermediate,
2822 src_type_split, zone_);
2823 EmitMove(intermediate, src.AsPairLocation()->At(i), temp);
2824 EmitNativeMove(dst.Split(i, zone_), intermediate_native, temp);
2825 }
2826 } else {
2827 const auto& intermediate_native =
2828 compiler::ffi::NativeLocation::FromLocation(intermediate, src_type,
2829 zone_);
2830 EmitMove(intermediate, src, temp);
2831 EmitNativeMove(dst, intermediate_native, temp);
2832 }
2833
2834 if (dst_type.IsInt() && TMP == kNoRegister) {
2835 temp->ReleaseTemporary();
2836 }
2837 }
2838 return;
2839}
2840
2841// The assignment to loading units here must match that in
2842// AssignLoadingUnitsCodeVisitor, which runs after compilation is done.
2843static intptr_t LoadingUnitOf(Zone* zone, const Function& function) {
2844 const Class& cls = Class::Handle(zone, function.Owner());
2845 const Library& lib = Library::Handle(zone, cls.library());
2846 const LoadingUnit& unit = LoadingUnit::Handle(zone, lib.loading_unit());
2847 ASSERT(!unit.IsNull());
2848 return unit.id();
2849}
2850
2851static intptr_t LoadingUnitOf(Zone* zone, const Code& code) {
2852 // No WeakSerializationReference owners here because those are only
2853 // introduced during AOT serialization.
2854 if (code.IsStubCode() || code.IsTypeTestStubCode()) {
2855 return LoadingUnit::kRootId;
2856 } else if (code.IsAllocationStubCode()) {
2857 const Class& cls = Class::Cast(Object::Handle(zone, code.owner()));
2858 const Library& lib = Library::Handle(zone, cls.library());
2859 const LoadingUnit& unit = LoadingUnit::Handle(zone, lib.loading_unit());
2860 ASSERT(!unit.IsNull());
2861 return unit.id();
2862 } else if (code.IsFunctionCode()) {
2863 return LoadingUnitOf(zone,
2864 Function::Cast(Object::Handle(zone, code.owner())));
2865 } else {
2866 UNREACHABLE();
2867 return LoadingUnit::kIllegalId;
2868 }
2869}
2870
2871bool FlowGraphCompiler::CanPcRelativeCall(const Function& target) const {
2872 return FLAG_precompiled_mode && FLAG_use_bare_instructions &&
2873 (LoadingUnitOf(zone_, function()) == LoadingUnitOf(zone_, target));
2874}
2875
2876bool FlowGraphCompiler::CanPcRelativeCall(const Code& target) const {
2877 return FLAG_precompiled_mode && FLAG_use_bare_instructions &&
2878 !target.InVMIsolateHeap() &&
2879 (LoadingUnitOf(zone_, function()) == LoadingUnitOf(zone_, target));
2880}
2881
2882bool FlowGraphCompiler::CanPcRelativeCall(const AbstractType& target) const {
2883 return FLAG_precompiled_mode && FLAG_use_bare_instructions &&
2884 !target.InVMIsolateHeap() &&
2885 (LoadingUnitOf(zone_, function()) == LoadingUnit::kRootId);
2886}
2887
2888#undef __
2889
2890} // namespace dart
2891