1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_COMPILER_H_ |
6 | #define RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_COMPILER_H_ |
7 | |
8 | #if defined(DART_PRECOMPILED_RUNTIME) |
9 | #error "AOT runtime should not use compiler sources (including header files)" |
10 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
11 | |
12 | #include <functional> |
13 | |
14 | #include "vm/allocation.h" |
15 | #include "vm/code_descriptors.h" |
16 | #include "vm/compiler/assembler/assembler.h" |
17 | #include "vm/compiler/backend/code_statistics.h" |
18 | #include "vm/compiler/backend/il.h" |
19 | #include "vm/compiler/backend/locations.h" |
20 | #include "vm/runtime_entry.h" |
21 | |
22 | namespace dart { |
23 | |
24 | // Forward declarations. |
25 | class CatchEntryMovesMapBuilder; |
26 | class Code; |
27 | class DeoptInfoBuilder; |
28 | class FlowGraph; |
29 | class FlowGraphCompiler; |
30 | class Function; |
31 | template <typename T> |
32 | class GrowableArray; |
33 | class ParsedFunction; |
34 | class SpeculativeInliningPolicy; |
35 | |
36 | namespace compiler { |
37 | struct TableSelector; |
38 | } |
39 | |
40 | // Used in methods which need conditional access to a temporary register. |
41 | // May only be used to allocate a single temporary register. |
42 | class TemporaryRegisterAllocator : public ValueObject { |
43 | public: |
44 | virtual ~TemporaryRegisterAllocator() {} |
45 | virtual Register AllocateTemporary() = 0; |
46 | virtual void ReleaseTemporary() = 0; |
47 | }; |
48 | |
49 | class ConstantTemporaryAllocator : public TemporaryRegisterAllocator { |
50 | public: |
51 | explicit ConstantTemporaryAllocator(Register tmp) : tmp_(tmp) {} |
52 | |
53 | Register AllocateTemporary() override { return tmp_; } |
54 | void ReleaseTemporary() override {} |
55 | |
56 | private: |
57 | Register const tmp_; |
58 | }; |
59 | |
60 | class NoTemporaryAllocator : public TemporaryRegisterAllocator { |
61 | public: |
62 | Register AllocateTemporary() override { UNREACHABLE(); } |
63 | void ReleaseTemporary() override { UNREACHABLE(); } |
64 | }; |
65 | |
66 | class ParallelMoveResolver : public ValueObject { |
67 | public: |
68 | explicit ParallelMoveResolver(FlowGraphCompiler* compiler); |
69 | |
70 | // Resolve a set of parallel moves, emitting assembler instructions. |
71 | void EmitNativeCode(ParallelMoveInstr* parallel_move); |
72 | |
73 | private: |
74 | class ScratchFpuRegisterScope : public ValueObject { |
75 | public: |
76 | ScratchFpuRegisterScope(ParallelMoveResolver* resolver, |
77 | FpuRegister blocked); |
78 | ~ScratchFpuRegisterScope(); |
79 | |
80 | FpuRegister reg() const { return reg_; } |
81 | |
82 | private: |
83 | ParallelMoveResolver* resolver_; |
84 | FpuRegister reg_; |
85 | bool spilled_; |
86 | }; |
87 | |
88 | class TemporaryAllocator : public TemporaryRegisterAllocator { |
89 | public: |
90 | TemporaryAllocator(ParallelMoveResolver* resolver, Register blocked); |
91 | |
92 | Register AllocateTemporary() override; |
93 | void ReleaseTemporary() override; |
94 | DEBUG_ONLY(bool DidAllocateTemporary() { return allocated_; }) |
95 | |
96 | virtual ~TemporaryAllocator() { ASSERT(reg_ == kNoRegister); } |
97 | |
98 | private: |
99 | ParallelMoveResolver* const resolver_; |
100 | const Register blocked_; |
101 | Register reg_; |
102 | bool spilled_; |
103 | DEBUG_ONLY(bool allocated_ = false); |
104 | }; |
105 | |
106 | class ScratchRegisterScope : public ValueObject { |
107 | public: |
108 | ScratchRegisterScope(ParallelMoveResolver* resolver, Register blocked); |
109 | ~ScratchRegisterScope(); |
110 | |
111 | Register reg() const { return reg_; } |
112 | |
113 | private: |
114 | TemporaryAllocator allocator_; |
115 | Register reg_; |
116 | }; |
117 | |
118 | bool IsScratchLocation(Location loc); |
119 | intptr_t AllocateScratchRegister(Location::Kind kind, |
120 | uword blocked_mask, |
121 | intptr_t first_free_register, |
122 | intptr_t last_free_register, |
123 | bool* spilled); |
124 | |
125 | void SpillScratch(Register reg); |
126 | void RestoreScratch(Register reg); |
127 | void SpillFpuScratch(FpuRegister reg); |
128 | void RestoreFpuScratch(FpuRegister reg); |
129 | |
130 | // friend class ScratchXmmRegisterScope; |
131 | |
132 | // Build the initial list of moves. |
133 | void BuildInitialMoveList(ParallelMoveInstr* parallel_move); |
134 | |
135 | // Perform the move at the moves_ index in question (possibly requiring |
136 | // other moves to satisfy dependencies). |
137 | void PerformMove(int index); |
138 | |
139 | // Emit a move and remove it from the move graph. |
140 | void EmitMove(int index); |
141 | |
142 | // Execute a move by emitting a swap of two operands. The move from |
143 | // source to destination is removed from the move graph. |
144 | void EmitSwap(int index); |
145 | |
146 | // Verify the move list before performing moves. |
147 | void Verify(); |
148 | |
149 | // Helpers for non-trivial source-destination combinations that cannot |
150 | // be handled by a single instruction. |
151 | void MoveMemoryToMemory(const compiler::Address& dst, |
152 | const compiler::Address& src); |
153 | void Exchange(Register reg, const compiler::Address& mem); |
154 | void Exchange(const compiler::Address& mem1, const compiler::Address& mem2); |
155 | void Exchange(Register reg, Register base_reg, intptr_t stack_offset); |
156 | void Exchange(Register base_reg1, |
157 | intptr_t stack_offset1, |
158 | Register base_reg2, |
159 | intptr_t stack_offset2); |
160 | |
161 | FlowGraphCompiler* compiler_; |
162 | |
163 | // List of moves not yet resolved. |
164 | GrowableArray<MoveOperands*> moves_; |
165 | }; |
166 | |
167 | // Used for describing a deoptimization point after call (lazy deoptimization). |
168 | // For deoptimization before instruction use class CompilerDeoptInfoWithStub. |
169 | class CompilerDeoptInfo : public ZoneAllocated { |
170 | public: |
171 | CompilerDeoptInfo(intptr_t deopt_id, |
172 | ICData::DeoptReasonId reason, |
173 | uint32_t flags, |
174 | Environment* deopt_env) |
175 | : pc_offset_(-1), |
176 | deopt_id_(deopt_id), |
177 | reason_(reason), |
178 | flags_(flags), |
179 | deopt_env_(deopt_env) { |
180 | ASSERT(deopt_env != NULL); |
181 | } |
182 | virtual ~CompilerDeoptInfo() {} |
183 | |
184 | TypedDataPtr CreateDeoptInfo(FlowGraphCompiler* compiler, |
185 | DeoptInfoBuilder* builder, |
186 | const Array& deopt_table); |
187 | |
188 | // No code needs to be generated. |
189 | virtual void GenerateCode(FlowGraphCompiler* compiler, intptr_t stub_ix) {} |
190 | |
191 | intptr_t pc_offset() const { return pc_offset_; } |
192 | void set_pc_offset(intptr_t offset) { pc_offset_ = offset; } |
193 | |
194 | intptr_t deopt_id() const { return deopt_id_; } |
195 | ICData::DeoptReasonId reason() const { return reason_; } |
196 | uint32_t flags() const { return flags_; } |
197 | const Environment* deopt_env() const { return deopt_env_; } |
198 | |
199 | private: |
200 | void EmitMaterializations(Environment* env, DeoptInfoBuilder* builder); |
201 | |
202 | void AllocateIncomingParametersRecursive(Environment* env, |
203 | intptr_t* stack_height); |
204 | |
205 | intptr_t pc_offset_; |
206 | const intptr_t deopt_id_; |
207 | const ICData::DeoptReasonId reason_; |
208 | const uint32_t flags_; |
209 | Environment* deopt_env_; |
210 | |
211 | DISALLOW_COPY_AND_ASSIGN(CompilerDeoptInfo); |
212 | }; |
213 | |
214 | class CompilerDeoptInfoWithStub : public CompilerDeoptInfo { |
215 | public: |
216 | CompilerDeoptInfoWithStub(intptr_t deopt_id, |
217 | ICData::DeoptReasonId reason, |
218 | uint32_t flags, |
219 | Environment* deopt_env) |
220 | : CompilerDeoptInfo(deopt_id, reason, flags, deopt_env), entry_label_() { |
221 | ASSERT(reason != ICData::kDeoptAtCall); |
222 | } |
223 | |
224 | compiler::Label* entry_label() { return &entry_label_; } |
225 | |
226 | // Implementation is in architecture specific file. |
227 | virtual void GenerateCode(FlowGraphCompiler* compiler, intptr_t stub_ix); |
228 | |
229 | const char* Name() const { |
230 | const char* kFormat = "Deopt stub for id %d, reason: %s" ; |
231 | const intptr_t len = Utils::SNPrint(NULL, 0, kFormat, deopt_id(), |
232 | DeoptReasonToCString(reason())) + |
233 | 1; |
234 | char* chars = Thread::Current()->zone()->Alloc<char>(len); |
235 | Utils::SNPrint(chars, len, kFormat, deopt_id(), |
236 | DeoptReasonToCString(reason())); |
237 | return chars; |
238 | } |
239 | |
240 | private: |
241 | compiler::Label entry_label_; |
242 | |
243 | DISALLOW_COPY_AND_ASSIGN(CompilerDeoptInfoWithStub); |
244 | }; |
245 | |
246 | class SlowPathCode : public ZoneAllocated { |
247 | public: |
248 | explicit SlowPathCode(Instruction* instruction) |
249 | : instruction_(instruction), entry_label_(), exit_label_() {} |
250 | virtual ~SlowPathCode() {} |
251 | |
252 | Instruction* instruction() const { return instruction_; } |
253 | compiler::Label* entry_label() { return &entry_label_; } |
254 | compiler::Label* exit_label() { return &exit_label_; } |
255 | |
256 | void GenerateCode(FlowGraphCompiler* compiler) { |
257 | EmitNativeCode(compiler); |
258 | ASSERT(entry_label_.IsBound()); |
259 | } |
260 | |
261 | private: |
262 | virtual void EmitNativeCode(FlowGraphCompiler* compiler) = 0; |
263 | |
264 | Instruction* instruction_; |
265 | compiler::Label entry_label_; |
266 | compiler::Label exit_label_; |
267 | |
268 | DISALLOW_COPY_AND_ASSIGN(SlowPathCode); |
269 | }; |
270 | |
271 | template <typename T> |
272 | class TemplateSlowPathCode : public SlowPathCode { |
273 | public: |
274 | explicit TemplateSlowPathCode(T* instruction) : SlowPathCode(instruction) {} |
275 | |
276 | T* instruction() const { |
277 | return static_cast<T*>(SlowPathCode::instruction()); |
278 | } |
279 | }; |
280 | |
281 | // Slow path code which calls runtime entry to throw an exception. |
282 | class ThrowErrorSlowPathCode : public TemplateSlowPathCode<Instruction> { |
283 | public: |
284 | ThrowErrorSlowPathCode(Instruction* instruction, |
285 | const RuntimeEntry& runtime_entry, |
286 | intptr_t num_args, |
287 | intptr_t try_index) |
288 | : TemplateSlowPathCode(instruction), |
289 | runtime_entry_(runtime_entry), |
290 | num_args_(num_args), |
291 | try_index_(try_index) {} |
292 | |
293 | // This name appears in disassembly. |
294 | virtual const char* name() = 0; |
295 | |
296 | // Subclasses can override these methods to customize slow path code. |
297 | virtual void EmitCodeAtSlowPathEntry(FlowGraphCompiler* compiler) {} |
298 | virtual void AddMetadataForRuntimeCall(FlowGraphCompiler* compiler) {} |
299 | |
300 | virtual void EmitSharedStubCall(FlowGraphCompiler* compiler, |
301 | bool save_fpu_registers) { |
302 | UNREACHABLE(); |
303 | } |
304 | |
305 | virtual void EmitNativeCode(FlowGraphCompiler* compiler); |
306 | |
307 | private: |
308 | const RuntimeEntry& runtime_entry_; |
309 | const intptr_t num_args_; |
310 | const intptr_t try_index_; |
311 | }; |
312 | |
313 | class NullErrorSlowPath : public ThrowErrorSlowPathCode { |
314 | public: |
315 | static const intptr_t kNumberOfArguments = 0; |
316 | |
317 | NullErrorSlowPath(CheckNullInstr* instruction, intptr_t try_index) |
318 | : ThrowErrorSlowPathCode(instruction, |
319 | GetRuntimeEntry(instruction->exception_type()), |
320 | kNumberOfArguments, |
321 | try_index) {} |
322 | |
323 | CheckNullInstr::ExceptionType exception_type() const { |
324 | return instruction()->AsCheckNull()->exception_type(); |
325 | } |
326 | |
327 | const char* name() override; |
328 | |
329 | void EmitSharedStubCall(FlowGraphCompiler* compiler, |
330 | bool save_fpu_registers) override; |
331 | |
332 | void AddMetadataForRuntimeCall(FlowGraphCompiler* compiler) override { |
333 | CheckNullInstr::AddMetadataForRuntimeCall(instruction()->AsCheckNull(), |
334 | compiler); |
335 | } |
336 | |
337 | static CodePtr GetStub(FlowGraphCompiler* compiler, |
338 | CheckNullInstr::ExceptionType exception_type, |
339 | bool save_fpu_registers); |
340 | |
341 | private: |
342 | static const RuntimeEntry& GetRuntimeEntry( |
343 | CheckNullInstr::ExceptionType exception_type); |
344 | }; |
345 | |
346 | class RangeErrorSlowPath : public ThrowErrorSlowPathCode { |
347 | public: |
348 | static const intptr_t kNumberOfArguments = 0; |
349 | |
350 | RangeErrorSlowPath(GenericCheckBoundInstr* instruction, intptr_t try_index) |
351 | : ThrowErrorSlowPathCode(instruction, |
352 | kRangeErrorRuntimeEntry, |
353 | kNumberOfArguments, |
354 | try_index) {} |
355 | virtual const char* name() { return "check bound" ; } |
356 | |
357 | virtual void EmitSharedStubCall(FlowGraphCompiler* compielr, |
358 | bool save_fpu_registers); |
359 | }; |
360 | |
361 | class FlowGraphCompiler : public ValueObject { |
362 | private: |
363 | class BlockInfo : public ZoneAllocated { |
364 | public: |
365 | BlockInfo() |
366 | : block_label_(), |
367 | jump_label_(&block_label_), |
368 | next_nonempty_label_(NULL), |
369 | is_marked_(false) {} |
370 | |
371 | // The label to jump to when control is transferred to this block. For |
372 | // nonempty blocks it is the label of the block itself. For empty |
373 | // blocks it is the label of the first nonempty successor block. |
374 | compiler::Label* jump_label() const { return jump_label_; } |
375 | void set_jump_label(compiler::Label* label) { jump_label_ = label; } |
376 | |
377 | // The label of the first nonempty block after this one in the block |
378 | // order, or NULL if there is no nonempty block following this one. |
379 | compiler::Label* next_nonempty_label() const { |
380 | return next_nonempty_label_; |
381 | } |
382 | void set_next_nonempty_label(compiler::Label* label) { |
383 | next_nonempty_label_ = label; |
384 | } |
385 | |
386 | bool WasCompacted() const { return jump_label_ != &block_label_; } |
387 | |
388 | // Block compaction is recursive. Block info for already-compacted |
389 | // blocks is marked so as to avoid cycles in the graph. |
390 | bool is_marked() const { return is_marked_; } |
391 | void mark() { is_marked_ = true; } |
392 | |
393 | private: |
394 | compiler::Label block_label_; |
395 | |
396 | compiler::Label* jump_label_; |
397 | compiler::Label* next_nonempty_label_; |
398 | |
399 | bool is_marked_; |
400 | }; |
401 | |
402 | public: |
403 | FlowGraphCompiler(compiler::Assembler* assembler, |
404 | FlowGraph* flow_graph, |
405 | const ParsedFunction& parsed_function, |
406 | bool is_optimizing, |
407 | SpeculativeInliningPolicy* speculative_policy, |
408 | const GrowableArray<const Function*>& inline_id_to_function, |
409 | const GrowableArray<TokenPosition>& inline_id_to_token_pos, |
410 | const GrowableArray<intptr_t>& caller_inline_id, |
411 | ZoneGrowableArray<const ICData*>* deopt_id_to_ic_data, |
412 | CodeStatistics* stats = NULL); |
413 | |
414 | void ArchSpecificInitialization(); |
415 | |
416 | ~FlowGraphCompiler(); |
417 | |
418 | static bool SupportsUnboxedDoubles(); |
419 | static bool SupportsUnboxedInt64(); |
420 | static bool SupportsUnboxedSimd128(); |
421 | static bool SupportsHardwareDivision(); |
422 | static bool CanConvertInt64ToDouble(); |
423 | |
424 | static bool IsUnboxedField(const Field& field); |
425 | static bool IsPotentialUnboxedField(const Field& field); |
426 | |
427 | // Accessors. |
428 | compiler::Assembler* assembler() const { return assembler_; } |
429 | const ParsedFunction& parsed_function() const { return parsed_function_; } |
430 | const Function& function() const { return parsed_function_.function(); } |
431 | const GrowableArray<BlockEntryInstr*>& block_order() const { |
432 | return block_order_; |
433 | } |
434 | const GrowableArray<const compiler::TableSelector*>& |
435 | dispatch_table_call_targets() const { |
436 | return dispatch_table_call_targets_; |
437 | } |
438 | |
439 | // If 'ForcedOptimization()' returns 'true', we are compiling in optimized |
440 | // mode for a function which cannot deoptimize. Certain optimizations, e.g. |
441 | // speculative optimizations and call patching are disabled. |
442 | bool ForcedOptimization() const { return function().ForceOptimize(); } |
443 | |
444 | const FlowGraph& flow_graph() const { return flow_graph_; } |
445 | |
446 | BlockEntryInstr* current_block() const { return current_block_; } |
447 | void set_current_block(BlockEntryInstr* value) { current_block_ = value; } |
448 | static bool CanOptimize(); |
449 | bool CanOptimizeFunction() const; |
450 | bool CanOSRFunction() const; |
451 | bool is_optimizing() const { return is_optimizing_; } |
452 | |
453 | void InsertBSSRelocation(BSS::Relocation reloc); |
454 | void LoadBSSEntry(BSS::Relocation relocation, Register dst, Register tmp); |
455 | |
456 | // The function was fully intrinsified, so the body is unreachable. |
457 | // |
458 | // We still need to compile the body in unoptimized mode because the |
459 | // 'ICData's are added to the function's 'ic_data_array_' when instance |
460 | // calls are compiled. |
461 | bool skip_body_compilation() const { |
462 | return fully_intrinsified_ && is_optimizing(); |
463 | } |
464 | |
465 | void EnterIntrinsicMode(); |
466 | void ExitIntrinsicMode(); |
467 | bool intrinsic_mode() const { return intrinsic_mode_; } |
468 | |
469 | void set_intrinsic_slow_path_label(compiler::Label* label) { |
470 | ASSERT(intrinsic_slow_path_label_ == nullptr || label == nullptr); |
471 | intrinsic_slow_path_label_ = label; |
472 | } |
473 | compiler::Label* intrinsic_slow_path_label() const { |
474 | ASSERT(intrinsic_slow_path_label_ != nullptr); |
475 | return intrinsic_slow_path_label_; |
476 | } |
477 | |
478 | bool ForceSlowPathForStackOverflow() const; |
479 | |
480 | const GrowableArray<BlockInfo*>& block_info() const { return block_info_; } |
481 | ParallelMoveResolver* parallel_move_resolver() { |
482 | return ¶llel_move_resolver_; |
483 | } |
484 | |
485 | void StatsBegin(Instruction* instr) { |
486 | if (stats_ != NULL) stats_->Begin(instr); |
487 | } |
488 | |
489 | void StatsEnd(Instruction* instr) { |
490 | if (stats_ != NULL) stats_->End(instr); |
491 | } |
492 | |
493 | void SpecialStatsBegin(intptr_t tag) { |
494 | if (stats_ != NULL) stats_->SpecialBegin(tag); |
495 | } |
496 | |
497 | void SpecialStatsEnd(intptr_t tag) { |
498 | if (stats_ != NULL) stats_->SpecialEnd(tag); |
499 | } |
500 | |
501 | GrowableArray<const Field*>& used_static_fields() { |
502 | return used_static_fields_; |
503 | } |
504 | |
505 | // Constructor is lighweight, major initialization work should occur here. |
506 | // This makes it easier to measure time spent in the compiler. |
507 | void InitCompiler(); |
508 | |
509 | void CompileGraph(); |
510 | |
511 | void EmitPrologue(); |
512 | |
513 | void VisitBlocks(); |
514 | |
515 | // Bail out of the flow graph compiler. Does not return to the caller. |
516 | void Bailout(const char* reason); |
517 | |
518 | // Returns 'true' if regular code generation should be skipped. |
519 | bool TryIntrinsify(); |
520 | |
521 | // Emits code for a generic move from a location 'src' to a location 'dst'. |
522 | // |
523 | // Note that Location does not include a size (that can only be deduced from |
524 | // a Representation), so these moves might overapproximate the size needed |
525 | // to move. The maximal overapproximation is moving 8 bytes instead of 4 on |
526 | // 64 bit architectures. This overapproximation is not a problem, because |
527 | // the Dart calling convention only uses word-sized stack slots. |
528 | // |
529 | // TODO(dartbug.com/40400): Express this in terms of EmitMove(NativeLocation |
530 | // NativeLocation) to remove code duplication. |
531 | void EmitMove(Location dst, Location src, TemporaryRegisterAllocator* temp); |
532 | |
533 | // Emits code for a move from a location `src` to a location `dst`. |
534 | // |
535 | // Takes into account the payload and container representations of `dst` and |
536 | // `src` to do the smallest move possible, and sign (or zero) extend or |
537 | // truncate if needed. |
538 | // |
539 | // Makes use of TMP, FpuTMP, and `temp`. |
540 | void EmitNativeMove(const compiler::ffi::NativeLocation& dst, |
541 | const compiler::ffi::NativeLocation& src, |
542 | TemporaryRegisterAllocator* temp); |
543 | |
544 | // Helper method to move from a Location to a NativeLocation. |
545 | void EmitMoveToNative(const compiler::ffi::NativeLocation& dst, |
546 | Location src_loc, |
547 | Representation src_type, |
548 | TemporaryRegisterAllocator* temp); |
549 | |
550 | // Helper method to move from a NativeLocation to a Location. |
551 | void EmitMoveFromNative(Location dst_loc, |
552 | Representation dst_type, |
553 | const compiler::ffi::NativeLocation& src, |
554 | TemporaryRegisterAllocator* temp); |
555 | |
556 | // Emits a Dart const to a native location. |
557 | void EmitMoveConst(const compiler::ffi::NativeLocation& dst, |
558 | Location src, |
559 | Representation src_type, |
560 | TemporaryRegisterAllocator* temp); |
561 | |
562 | bool CheckAssertAssignableTypeTestingABILocations( |
563 | const LocationSummary& locs); |
564 | |
565 | void GenerateAssertAssignable(CompileType* receiver_type, |
566 | TokenPosition token_pos, |
567 | intptr_t deopt_id, |
568 | const String& dst_name, |
569 | LocationSummary* locs); |
570 | |
571 | // Returns true if we can use a type testing stub based assert |
572 | // assignable code pattern for the given type. |
573 | static bool ShouldUseTypeTestingStubFor(bool optimizing, |
574 | const AbstractType& type); |
575 | |
576 | void GenerateAssertAssignableViaTypeTestingStub(CompileType* receiver_type, |
577 | TokenPosition token_pos, |
578 | intptr_t deopt_id, |
579 | const String& dst_name, |
580 | LocationSummary* locs); |
581 | |
582 | void GenerateAssertAssignableViaTypeTestingStub( |
583 | CompileType* receiver_type, |
584 | const AbstractType& dst_type, |
585 | const String& dst_name, |
586 | const Register dst_type_reg_to_call, |
587 | const Register scratch_reg, |
588 | compiler::Label* done); |
589 | |
590 | void GenerateRuntimeCall(TokenPosition token_pos, |
591 | intptr_t deopt_id, |
592 | const RuntimeEntry& entry, |
593 | intptr_t argument_count, |
594 | LocationSummary* locs); |
595 | |
596 | void GenerateStubCall(TokenPosition token_pos, |
597 | const Code& stub, |
598 | PcDescriptorsLayout::Kind kind, |
599 | LocationSummary* locs, |
600 | intptr_t deopt_id = DeoptId::kNone, |
601 | Environment* env = nullptr); |
602 | |
603 | void GeneratePatchableCall(TokenPosition token_pos, |
604 | const Code& stub, |
605 | PcDescriptorsLayout::Kind kind, |
606 | LocationSummary* locs); |
607 | |
608 | void GenerateDartCall(intptr_t deopt_id, |
609 | TokenPosition token_pos, |
610 | const Code& stub, |
611 | PcDescriptorsLayout::Kind kind, |
612 | LocationSummary* locs, |
613 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
614 | |
615 | void GenerateStaticDartCall( |
616 | intptr_t deopt_id, |
617 | TokenPosition token_pos, |
618 | PcDescriptorsLayout::Kind kind, |
619 | LocationSummary* locs, |
620 | const Function& target, |
621 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
622 | |
623 | void GenerateInstanceOf(TokenPosition token_pos, |
624 | intptr_t deopt_id, |
625 | const AbstractType& type, |
626 | LocationSummary* locs); |
627 | |
628 | void GenerateInstanceCall(intptr_t deopt_id, |
629 | TokenPosition token_pos, |
630 | LocationSummary* locs, |
631 | const ICData& ic_data, |
632 | Code::EntryKind entry_kind, |
633 | bool receiver_can_be_smi); |
634 | |
635 | void GenerateStaticCall( |
636 | intptr_t deopt_id, |
637 | TokenPosition token_pos, |
638 | const Function& function, |
639 | ArgumentsInfo args_info, |
640 | LocationSummary* locs, |
641 | const ICData& ic_data_in, |
642 | ICData::RebindRule rebind_rule, |
643 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
644 | |
645 | void GenerateNumberTypeCheck(Register kClassIdReg, |
646 | const AbstractType& type, |
647 | compiler::Label* is_instance_lbl, |
648 | compiler::Label* is_not_instance_lbl); |
649 | void GenerateStringTypeCheck(Register kClassIdReg, |
650 | compiler::Label* is_instance_lbl, |
651 | compiler::Label* is_not_instance_lbl); |
652 | void GenerateListTypeCheck(Register kClassIdReg, |
653 | compiler::Label* is_instance_lbl); |
654 | |
655 | // Returns true if no further checks are necessary but the code coming after |
656 | // the emitted code here is still required do a runtime call (for the negative |
657 | // case of throwing an exception). |
658 | bool GenerateSubtypeRangeCheck(Register class_id_reg, |
659 | const Class& type_class, |
660 | compiler::Label* is_subtype_lbl); |
661 | |
662 | // We test up to 4 different cid ranges, if we would need to test more in |
663 | // order to get a definite answer we fall back to the old mechanism (namely |
664 | // of going into the subtyping cache) |
665 | static const intptr_t kMaxNumberOfCidRangesToTest = 4; |
666 | |
667 | // If [fall_through_if_inside] is `true`, then [outside_range_lbl] must be |
668 | // supplied, since it will be jumped to in the last case if the cid is outside |
669 | // the range. |
670 | static void GenerateCidRangesCheck(compiler::Assembler* assembler, |
671 | Register class_id_reg, |
672 | const CidRangeVector& cid_ranges, |
673 | compiler::Label* inside_range_lbl, |
674 | compiler::Label* outside_range_lbl = NULL, |
675 | bool fall_through_if_inside = false); |
676 | |
677 | void EmitOptimizedInstanceCall( |
678 | const Code& stub, |
679 | const ICData& ic_data, |
680 | intptr_t deopt_id, |
681 | TokenPosition token_pos, |
682 | LocationSummary* locs, |
683 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
684 | |
685 | void EmitInstanceCallJIT(const Code& stub, |
686 | const ICData& ic_data, |
687 | intptr_t deopt_id, |
688 | TokenPosition token_pos, |
689 | LocationSummary* locs, |
690 | Code::EntryKind entry_kind); |
691 | |
692 | void EmitPolymorphicInstanceCall(const PolymorphicInstanceCallInstr* call, |
693 | const CallTargets& targets, |
694 | ArgumentsInfo args_info, |
695 | intptr_t deopt_id, |
696 | TokenPosition token_pos, |
697 | LocationSummary* locs, |
698 | bool complete, |
699 | intptr_t total_call_count, |
700 | bool receiver_can_be_smi = true); |
701 | |
702 | void EmitMegamorphicInstanceCall(const ICData& icdata, |
703 | intptr_t deopt_id, |
704 | TokenPosition token_pos, |
705 | LocationSummary* locs, |
706 | intptr_t try_index, |
707 | intptr_t slow_path_argument_count = 0) { |
708 | const String& name = String::Handle(icdata.target_name()); |
709 | const Array& arguments_descriptor = |
710 | Array::Handle(icdata.arguments_descriptor()); |
711 | EmitMegamorphicInstanceCall(name, arguments_descriptor, deopt_id, token_pos, |
712 | locs, try_index); |
713 | } |
714 | |
715 | // Pass a value for try-index where block is not available (e.g. slow path). |
716 | void EmitMegamorphicInstanceCall(const String& function_name, |
717 | const Array& arguments_descriptor, |
718 | intptr_t deopt_id, |
719 | TokenPosition token_pos, |
720 | LocationSummary* locs, |
721 | intptr_t try_index, |
722 | intptr_t slow_path_argument_count = 0); |
723 | |
724 | void EmitInstanceCallAOT( |
725 | const ICData& ic_data, |
726 | intptr_t deopt_id, |
727 | TokenPosition token_pos, |
728 | LocationSummary* locs, |
729 | Code::EntryKind entry_kind = Code::EntryKind::kNormal, |
730 | bool receiver_can_be_smi = true); |
731 | |
732 | void EmitTestAndCall(const CallTargets& targets, |
733 | const String& function_name, |
734 | ArgumentsInfo args_info, |
735 | compiler::Label* failed, |
736 | compiler::Label* match_found, |
737 | intptr_t deopt_id, |
738 | TokenPosition token_index, |
739 | LocationSummary* locs, |
740 | bool complete, |
741 | intptr_t total_ic_calls, |
742 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
743 | |
744 | void EmitDispatchTableCall(Register cid_reg, |
745 | int32_t selector_offset, |
746 | const Array& arguments_descriptor); |
747 | |
748 | Condition EmitEqualityRegConstCompare(Register reg, |
749 | const Object& obj, |
750 | bool needs_number_check, |
751 | TokenPosition token_pos, |
752 | intptr_t deopt_id); |
753 | Condition EmitEqualityRegRegCompare(Register left, |
754 | Register right, |
755 | bool needs_number_check, |
756 | TokenPosition token_pos, |
757 | intptr_t deopt_id); |
758 | Condition EmitBoolTest(Register value, BranchLabels labels, bool invert); |
759 | |
760 | bool NeedsEdgeCounter(BlockEntryInstr* block); |
761 | |
762 | void EmitEdgeCounter(intptr_t edge_id); |
763 | |
764 | void RecordCatchEntryMoves(Environment* env = NULL, |
765 | intptr_t try_index = kInvalidTryIndex); |
766 | |
767 | void EmitCallToStub(const Code& stub); |
768 | void EmitTailCallToStub(const Code& stub); |
769 | |
770 | // Emits the following metadata for the current PC: |
771 | // |
772 | // * Attaches current try index |
773 | // * Attaches stackmaps |
774 | // * Attaches catch entry moves (in AOT) |
775 | // * Deoptimization information (in JIT) |
776 | // |
777 | // If [env] is not `nullptr` it will be used instead of the |
778 | // `pending_deoptimization_env`. |
779 | void EmitCallsiteMetadata(TokenPosition token_pos, |
780 | intptr_t deopt_id, |
781 | PcDescriptorsLayout::Kind kind, |
782 | LocationSummary* locs, |
783 | Environment* env = nullptr); |
784 | |
785 | void EmitYieldPositionMetadata(TokenPosition token_pos, intptr_t yield_index); |
786 | |
787 | void (Instruction* instr); |
788 | |
789 | // Returns stack size (number of variables on stack for unoptimized |
790 | // code, or number of spill slots for optimized code). |
791 | intptr_t StackSize() const; |
792 | |
793 | // Returns the number of extra stack slots used during an Osr entry |
794 | // (values for all [ParameterInstr]s, representing local variables |
795 | // and expression stack values, are already on the stack). |
796 | intptr_t () const; |
797 | |
798 | // Returns assembler label associated with the given block entry. |
799 | compiler::Label* GetJumpLabel(BlockEntryInstr* block_entry) const; |
800 | bool WasCompacted(BlockEntryInstr* block_entry) const; |
801 | |
802 | // Returns the label of the fall-through of the current block. |
803 | compiler::Label* NextNonEmptyLabel() const; |
804 | |
805 | // Returns true if there is a next block after the current one in |
806 | // the block order and if it is the given block. |
807 | bool CanFallThroughTo(BlockEntryInstr* block_entry) const; |
808 | |
809 | // Return true-, false- and fall-through label for a branch instruction. |
810 | BranchLabels CreateBranchLabels(BranchInstr* branch) const; |
811 | |
812 | void AddExceptionHandler(intptr_t try_index, |
813 | intptr_t outer_try_index, |
814 | intptr_t pc_offset, |
815 | bool is_generated, |
816 | const Array& handler_types, |
817 | bool needs_stacktrace); |
818 | void SetNeedsStackTrace(intptr_t try_index); |
819 | void AddCurrentDescriptor(PcDescriptorsLayout::Kind kind, |
820 | intptr_t deopt_id, |
821 | TokenPosition token_pos); |
822 | void AddDescriptor( |
823 | PcDescriptorsLayout::Kind kind, |
824 | intptr_t pc_offset, |
825 | intptr_t deopt_id, |
826 | TokenPosition token_pos, |
827 | intptr_t try_index, |
828 | intptr_t yield_index = PcDescriptorsLayout::kInvalidYieldIndex); |
829 | |
830 | // Add NullCheck information for the current PC. |
831 | void AddNullCheck(TokenPosition token_pos, const String& name); |
832 | |
833 | void RecordSafepoint(LocationSummary* locs, |
834 | intptr_t slow_path_argument_count = 0); |
835 | |
836 | compiler::Label* AddDeoptStub(intptr_t deopt_id, |
837 | ICData::DeoptReasonId reason, |
838 | uint32_t flags = 0); |
839 | |
840 | CompilerDeoptInfo* AddDeoptIndexAtCall(intptr_t deopt_id); |
841 | CompilerDeoptInfo* AddSlowPathDeoptInfo(intptr_t deopt_id, Environment* env); |
842 | |
843 | void AddSlowPathCode(SlowPathCode* slow_path); |
844 | |
845 | void FinalizeExceptionHandlers(const Code& code); |
846 | void FinalizePcDescriptors(const Code& code); |
847 | ArrayPtr CreateDeoptInfo(compiler::Assembler* assembler); |
848 | void FinalizeStackMaps(const Code& code); |
849 | void FinalizeVarDescriptors(const Code& code); |
850 | void FinalizeCatchEntryMovesMap(const Code& code); |
851 | void FinalizeStaticCallTargetsTable(const Code& code); |
852 | void FinalizeCodeSourceMap(const Code& code); |
853 | |
854 | const Class& double_class() const { return double_class_; } |
855 | const Class& mint_class() const { return mint_class_; } |
856 | const Class& float32x4_class() const { return float32x4_class_; } |
857 | const Class& float64x2_class() const { return float64x2_class_; } |
858 | const Class& int32x4_class() const { return int32x4_class_; } |
859 | |
860 | const Class& BoxClassFor(Representation rep); |
861 | |
862 | void SaveLiveRegisters(LocationSummary* locs); |
863 | void RestoreLiveRegisters(LocationSummary* locs); |
864 | #if defined(DEBUG) |
865 | void ClobberDeadTempRegisters(LocationSummary* locs); |
866 | #endif |
867 | |
868 | // Returns a new environment based on [env] which accounts for the new |
869 | // locations of values in the slow path call. |
870 | Environment* SlowPathEnvironmentFor(Instruction* inst, |
871 | intptr_t num_slow_path_args) { |
872 | if (inst->env() == nullptr && is_optimizing()) { |
873 | if (pending_deoptimization_env_ == nullptr) { |
874 | return nullptr; |
875 | } |
876 | return SlowPathEnvironmentFor(pending_deoptimization_env_, inst->locs(), |
877 | num_slow_path_args); |
878 | } |
879 | return SlowPathEnvironmentFor(inst->env(), inst->locs(), |
880 | num_slow_path_args); |
881 | } |
882 | |
883 | Environment* SlowPathEnvironmentFor(Environment* env, |
884 | LocationSummary* locs, |
885 | intptr_t num_slow_path_args); |
886 | |
887 | intptr_t CurrentTryIndex() const { |
888 | if (current_block_ == NULL) { |
889 | return kInvalidTryIndex; |
890 | } |
891 | return current_block_->try_index(); |
892 | } |
893 | |
894 | bool may_reoptimize() const { return may_reoptimize_; } |
895 | |
896 | // Use in unoptimized compilation to preserve/reuse ICData. |
897 | const ICData* GetOrAddInstanceCallICData(intptr_t deopt_id, |
898 | const String& target_name, |
899 | const Array& arguments_descriptor, |
900 | intptr_t num_args_tested, |
901 | const AbstractType& receiver_type); |
902 | |
903 | const ICData* GetOrAddStaticCallICData(intptr_t deopt_id, |
904 | const Function& target, |
905 | const Array& arguments_descriptor, |
906 | intptr_t num_args_tested, |
907 | ICData::RebindRule rebind_rule); |
908 | |
909 | static const CallTargets* ResolveCallTargetsForReceiverCid( |
910 | intptr_t cid, |
911 | const String& selector, |
912 | const Array& args_desc_array); |
913 | |
914 | const ZoneGrowableArray<const ICData*>& deopt_id_to_ic_data() const { |
915 | return *deopt_id_to_ic_data_; |
916 | } |
917 | |
918 | Thread* thread() const { return thread_; } |
919 | Isolate* isolate() const { return thread_->isolate(); } |
920 | Zone* zone() const { return zone_; } |
921 | |
922 | void AddStubCallTarget(const Code& code); |
923 | void AddDispatchTableCallTarget(const compiler::TableSelector* selector); |
924 | |
925 | ArrayPtr edge_counters_array() const { return edge_counters_array_.raw(); } |
926 | |
927 | ArrayPtr InliningIdToFunction() const; |
928 | |
929 | void BeginCodeSourceRange(); |
930 | void EndCodeSourceRange(TokenPosition token_pos); |
931 | |
932 | static bool LookupMethodFor(int class_id, |
933 | const String& name, |
934 | const ArgumentsDescriptor& args_desc, |
935 | Function* fn_return, |
936 | bool* class_is_abstract_return = NULL); |
937 | |
938 | // Returns new class-id bias. |
939 | // |
940 | // TODO(kustermann): We should move this code out of the [FlowGraphCompiler]! |
941 | static int EmitTestAndCallCheckCid(compiler::Assembler* assembler, |
942 | compiler::Label* label, |
943 | Register class_id_reg, |
944 | const CidRangeValue& range, |
945 | int bias, |
946 | bool jump_on_miss = true); |
947 | |
948 | bool IsEmptyBlock(BlockEntryInstr* block) const; |
949 | |
950 | private: |
951 | friend class BoxInt64Instr; // For AddPcRelativeCallStubTarget(). |
952 | friend class CheckNullInstr; // For AddPcRelativeCallStubTarget(). |
953 | friend class NullErrorSlowPath; // For AddPcRelativeCallStubTarget(). |
954 | friend class CheckStackOverflowInstr; // For AddPcRelativeCallStubTarget(). |
955 | friend class StoreIndexedInstr; // For AddPcRelativeCallStubTarget(). |
956 | friend class StoreInstanceFieldInstr; // For AddPcRelativeCallStubTarget(). |
957 | friend class CheckStackOverflowSlowPath; // For pending_deoptimization_env_. |
958 | friend class CheckedSmiSlowPath; // Same. |
959 | friend class CheckedSmiComparisonSlowPath; // Same. |
960 | friend class GraphInstrinsicCodeGenScope; // For optimizing_. |
961 | |
962 | // Architecture specific implementation of simple native moves. |
963 | void EmitNativeMoveArchitecture(const compiler::ffi::NativeLocation& dst, |
964 | const compiler::ffi::NativeLocation& src); |
965 | |
966 | void EmitFrameEntry(); |
967 | |
968 | bool TryIntrinsifyHelper(); |
969 | void AddPcRelativeCallTarget(const Function& function, |
970 | Code::EntryKind entry_kind); |
971 | void AddPcRelativeCallStubTarget(const Code& stub_code); |
972 | void AddPcRelativeTailCallStubTarget(const Code& stub_code); |
973 | void AddPcRelativeTTSCallTypeTarget(const AbstractType& type); |
974 | void AddStaticCallTarget(const Function& function, |
975 | Code::EntryKind entry_kind); |
976 | |
977 | void GenerateDeferredCode(); |
978 | |
979 | void EmitInstructionPrologue(Instruction* instr); |
980 | void EmitInstructionEpilogue(Instruction* instr); |
981 | |
982 | // Emit code to load a Value into register 'dst'. |
983 | void LoadValue(Register dst, Value* value); |
984 | |
985 | void EmitOptimizedStaticCall( |
986 | const Function& function, |
987 | const Array& arguments_descriptor, |
988 | intptr_t size_with_type_args, |
989 | intptr_t deopt_id, |
990 | TokenPosition token_pos, |
991 | LocationSummary* locs, |
992 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
993 | |
994 | void EmitUnoptimizedStaticCall( |
995 | intptr_t size_with_type_args, |
996 | intptr_t deopt_id, |
997 | TokenPosition token_pos, |
998 | LocationSummary* locs, |
999 | const ICData& ic_data, |
1000 | Code::EntryKind entry_kind = Code::EntryKind::kNormal); |
1001 | |
1002 | // Helper for TestAndCall that calculates a good bias that |
1003 | // allows more compact instructions to be emitted. |
1004 | intptr_t ComputeGoodBiasForCidComparison(const CallTargets& sorted, |
1005 | intptr_t max_immediate); |
1006 | |
1007 | // More helpers for EmitTestAndCall. |
1008 | |
1009 | static Register EmitTestCidRegister(); |
1010 | |
1011 | void EmitTestAndCallLoadReceiver(intptr_t count_without_type_args, |
1012 | const Array& arguments_descriptor); |
1013 | |
1014 | void EmitTestAndCallSmiBranch(compiler::Label* label, bool jump_if_smi); |
1015 | |
1016 | void EmitTestAndCallLoadCid(Register class_id_reg); |
1017 | |
1018 | // Type checking helper methods. |
1019 | void CheckClassIds(Register class_id_reg, |
1020 | const GrowableArray<intptr_t>& class_ids, |
1021 | compiler::Label* is_instance_lbl, |
1022 | compiler::Label* is_not_instance_lbl); |
1023 | |
1024 | SubtypeTestCachePtr GenerateInlineInstanceof( |
1025 | TokenPosition token_pos, |
1026 | const AbstractType& type, |
1027 | compiler::Label* is_instance_lbl, |
1028 | compiler::Label* is_not_instance_lbl); |
1029 | |
1030 | SubtypeTestCachePtr GenerateInstantiatedTypeWithArgumentsTest( |
1031 | TokenPosition token_pos, |
1032 | const AbstractType& dst_type, |
1033 | compiler::Label* is_instance_lbl, |
1034 | compiler::Label* is_not_instance_lbl); |
1035 | |
1036 | bool GenerateInstantiatedTypeNoArgumentsTest( |
1037 | TokenPosition token_pos, |
1038 | const AbstractType& dst_type, |
1039 | compiler::Label* is_instance_lbl, |
1040 | compiler::Label* is_not_instance_lbl); |
1041 | |
1042 | SubtypeTestCachePtr GenerateUninstantiatedTypeTest( |
1043 | TokenPosition token_pos, |
1044 | const AbstractType& dst_type, |
1045 | compiler::Label* is_instance_lbl, |
1046 | compiler::Label* is_not_instance_label); |
1047 | |
1048 | SubtypeTestCachePtr GenerateFunctionTypeTest( |
1049 | TokenPosition token_pos, |
1050 | const AbstractType& dst_type, |
1051 | compiler::Label* is_instance_lbl, |
1052 | compiler::Label* is_not_instance_label); |
1053 | |
1054 | SubtypeTestCachePtr GenerateSubtype1TestCacheLookup( |
1055 | TokenPosition token_pos, |
1056 | const Class& type_class, |
1057 | compiler::Label* is_instance_lbl, |
1058 | compiler::Label* is_not_instance_lbl); |
1059 | |
1060 | enum TypeTestStubKind { |
1061 | kTestTypeOneArg, |
1062 | kTestTypeTwoArgs, |
1063 | kTestTypeFourArgs, |
1064 | kTestTypeSixArgs, |
1065 | }; |
1066 | |
1067 | // Returns type test stub kind for a type test against type parameter type. |
1068 | TypeTestStubKind GetTypeTestStubKindForTypeParameter( |
1069 | const TypeParameter& type_param); |
1070 | |
1071 | SubtypeTestCachePtr GenerateCallSubtypeTestStub( |
1072 | TypeTestStubKind test_kind, |
1073 | Register instance_reg, |
1074 | Register instantiator_type_arguments_reg, |
1075 | Register function_type_arguments_reg, |
1076 | Register temp_reg, |
1077 | compiler::Label* is_instance_lbl, |
1078 | compiler::Label* is_not_instance_lbl); |
1079 | |
1080 | void GenerateBoolToJump(Register bool_reg, |
1081 | compiler::Label* is_true, |
1082 | compiler::Label* is_false); |
1083 | |
1084 | void (const Function& , |
1085 | intptr_t type_arguments_field_offset); |
1086 | |
1087 | void GenerateGetterIntrinsic(const Function& accessor, const Field& field); |
1088 | |
1089 | // Perform a greedy local register allocation. Consider all registers free. |
1090 | void AllocateRegistersLocally(Instruction* instr); |
1091 | |
1092 | // Map a block number in a forward iteration into the block number in the |
1093 | // corresponding reverse iteration. Used to obtain an index into |
1094 | // block_order for reverse iterations. |
1095 | intptr_t reverse_index(intptr_t index) const { |
1096 | return block_order_.length() - index - 1; |
1097 | } |
1098 | |
1099 | void set_current_instruction(Instruction* current_instruction) { |
1100 | current_instruction_ = current_instruction; |
1101 | } |
1102 | |
1103 | Instruction* current_instruction() { return current_instruction_; } |
1104 | |
1105 | void CompactBlock(BlockEntryInstr* block); |
1106 | void CompactBlocks(); |
1107 | |
1108 | bool IsListClass(const Class& cls) const { |
1109 | return cls.raw() == list_class_.raw(); |
1110 | } |
1111 | |
1112 | void EmitSourceLine(Instruction* instr); |
1113 | |
1114 | intptr_t GetOptimizationThreshold() const; |
1115 | |
1116 | CompressedStackMapsBuilder* compressed_stackmaps_builder() { |
1117 | if (compressed_stackmaps_builder_ == NULL) { |
1118 | compressed_stackmaps_builder_ = new CompressedStackMapsBuilder(); |
1119 | } |
1120 | return compressed_stackmaps_builder_; |
1121 | } |
1122 | |
1123 | #if defined(DEBUG) |
1124 | void FrameStateUpdateWith(Instruction* instr); |
1125 | void FrameStatePush(Definition* defn); |
1126 | void FrameStatePop(intptr_t count); |
1127 | bool FrameStateIsSafeToCall(); |
1128 | void FrameStateClear(); |
1129 | #endif |
1130 | |
1131 | // Returns true if instruction lookahead (window size one) |
1132 | // is amenable to a peephole optimization. |
1133 | bool IsPeephole(Instruction* instr) const; |
1134 | |
1135 | #if defined(DEBUG) |
1136 | bool CanCallDart() const { |
1137 | return current_instruction_ == nullptr || |
1138 | current_instruction_->CanCallDart(); |
1139 | } |
1140 | #else |
1141 | bool CanCallDart() const { return true; } |
1142 | #endif |
1143 | |
1144 | bool CanPcRelativeCall(const Function& target) const; |
1145 | bool CanPcRelativeCall(const Code& target) const; |
1146 | bool CanPcRelativeCall(const AbstractType& target) const; |
1147 | |
1148 | // This struct contains either function or code, the other one being NULL. |
1149 | class StaticCallsStruct : public ZoneAllocated { |
1150 | public: |
1151 | Code::CallKind call_kind; |
1152 | Code::CallEntryPoint entry_point; |
1153 | const intptr_t offset; |
1154 | const Function* function; // Can be nullptr. |
1155 | const Code* code; // Can be nullptr. |
1156 | const AbstractType* dst_type; // Can be nullptr. |
1157 | StaticCallsStruct(Code::CallKind call_kind, |
1158 | Code::CallEntryPoint entry_point, |
1159 | intptr_t offset_arg, |
1160 | const Function* function_arg, |
1161 | const Code* code_arg, |
1162 | const AbstractType* dst_type) |
1163 | : call_kind(call_kind), |
1164 | entry_point(entry_point), |
1165 | offset(offset_arg), |
1166 | function(function_arg), |
1167 | code(code_arg), |
1168 | dst_type(dst_type) { |
1169 | ASSERT(function == nullptr || function->IsZoneHandle()); |
1170 | ASSERT(code == nullptr || code->IsZoneHandle() || |
1171 | code->IsReadOnlyHandle()); |
1172 | ASSERT(dst_type == nullptr || dst_type->IsZoneHandle() || |
1173 | dst_type->IsReadOnlyHandle()); |
1174 | ASSERT(code == nullptr || dst_type == nullptr); |
1175 | } |
1176 | |
1177 | private: |
1178 | DISALLOW_COPY_AND_ASSIGN(StaticCallsStruct); |
1179 | }; |
1180 | |
1181 | Thread* thread_; |
1182 | Zone* zone_; |
1183 | compiler::Assembler* assembler_; |
1184 | const ParsedFunction& parsed_function_; |
1185 | const FlowGraph& flow_graph_; |
1186 | const GrowableArray<BlockEntryInstr*>& block_order_; |
1187 | |
1188 | #if defined(DEBUG) |
1189 | GrowableArray<Representation> frame_state_; |
1190 | #endif |
1191 | |
1192 | // Compiler specific per-block state. Indexed by postorder block number |
1193 | // for convenience. This is not the block's index in the block order, |
1194 | // which is reverse postorder. |
1195 | BlockEntryInstr* current_block_; |
1196 | ExceptionHandlerList* exception_handlers_list_; |
1197 | DescriptorList* pc_descriptors_list_; |
1198 | CompressedStackMapsBuilder* compressed_stackmaps_builder_; |
1199 | CodeSourceMapBuilder* code_source_map_builder_; |
1200 | CatchEntryMovesMapBuilder* catch_entry_moves_maps_builder_; |
1201 | GrowableArray<BlockInfo*> block_info_; |
1202 | GrowableArray<CompilerDeoptInfo*> deopt_infos_; |
1203 | GrowableArray<SlowPathCode*> slow_path_code_; |
1204 | // Fields that were referenced by generated code. |
1205 | // This list is needed by precompiler to ensure they are retained. |
1206 | GrowableArray<const Field*> used_static_fields_; |
1207 | // Stores static call targets as well as stub targets. |
1208 | // TODO(srdjan): Evaluate if we should store allocation stub targets into a |
1209 | // separate table? |
1210 | GrowableArray<StaticCallsStruct*> static_calls_target_table_; |
1211 | // The table selectors of all dispatch table calls in the current function. |
1212 | GrowableArray<const compiler::TableSelector*> dispatch_table_call_targets_; |
1213 | GrowableArray<IndirectGotoInstr*> indirect_gotos_; |
1214 | bool is_optimizing_; |
1215 | SpeculativeInliningPolicy* speculative_policy_; |
1216 | // Set to true if optimized code has IC calls. |
1217 | bool may_reoptimize_; |
1218 | // True while emitting intrinsic code. |
1219 | bool intrinsic_mode_; |
1220 | compiler::Label* intrinsic_slow_path_label_ = nullptr; |
1221 | bool fully_intrinsified_ = false; |
1222 | CodeStatistics* stats_; |
1223 | |
1224 | // The definition whose value is supposed to be at the top of the |
1225 | // expression stack. Used by peephole optimization (window size one) |
1226 | // to eliminate redundant push/pop pairs. |
1227 | Definition* top_of_stack_ = nullptr; |
1228 | |
1229 | const Class& double_class_; |
1230 | const Class& mint_class_; |
1231 | const Class& float32x4_class_; |
1232 | const Class& float64x2_class_; |
1233 | const Class& int32x4_class_; |
1234 | const Class& list_class_; |
1235 | |
1236 | ParallelMoveResolver parallel_move_resolver_; |
1237 | |
1238 | // Currently instructions generate deopt stubs internally by |
1239 | // calling AddDeoptStub. To communicate deoptimization environment |
1240 | // that should be used when deoptimizing we store it in this variable. |
1241 | // In future AddDeoptStub should be moved out of the instruction template. |
1242 | Environment* pending_deoptimization_env_; |
1243 | |
1244 | ZoneGrowableArray<const ICData*>* deopt_id_to_ic_data_; |
1245 | Array& edge_counters_array_; |
1246 | |
1247 | // Instruction currently running EmitNativeCode(). |
1248 | Instruction* current_instruction_ = nullptr; |
1249 | |
1250 | DISALLOW_COPY_AND_ASSIGN(FlowGraphCompiler); |
1251 | }; |
1252 | |
1253 | } // namespace dart |
1254 | |
1255 | #endif // RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_COMPILER_H_ |
1256 | |