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