Flutter Engine
The Flutter Engine
flow_graph_builder.cc
Go to the documentation of this file.
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
6
12#include "vm/object.h"
13#include "vm/zone.h"
14
15namespace dart {
16
17// Quick access to the locally defined zone() method.
18#define Z (zone())
19
20// TODO(srdjan): Allow compiler to add constants as they are encountered in
21// the compilation.
22const double kCommonDoubleConstants[] = {
23 -1.0, -0.5, -0.1, 0.0, 0.1, 0.5, 1.0, 2.0, 4.0, 5.0, 10.0, 20.0, 30.0, 64.0,
24 255.0, NAN,
25 // From dart:math
26 2.718281828459045, 2.302585092994046, 0.6931471805599453,
27 1.4426950408889634, 0.4342944819032518, 3.1415926535897932,
28 0.7071067811865476, 1.4142135623730951};
29
31 intptr_t len = sizeof(kCommonDoubleConstants) / sizeof(double); // NOLINT
32 for (intptr_t i = 0; i < len; i++) {
34 return reinterpret_cast<uword>(&kCommonDoubleConstants[i]);
35 }
36 }
37 return 0;
38}
39
42 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
43 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id());
44 ASSERT(callee_graph->current_ssa_temp_index() >
45 caller_graph_->current_ssa_temp_index());
46
47 // Adjust the caller's maximum block id and current SSA temp index.
48 caller_graph_->set_max_block_id(callee_graph->max_block_id());
49 caller_graph_->set_current_ssa_temp_index(
50 callee_graph->current_ssa_temp_index());
51
52 // Attach the outer environment on each instruction in the callee graph.
53 ASSERT(call_->env() != nullptr);
54 ASSERT(call_->deopt_id() != DeoptId::kNone);
55
56 auto zone = callee_graph->zone();
57 auto env = call_->env();
58
59 const intptr_t outer_deopt_id = call_->deopt_id();
60 // Scale the edge weights by the call count for the inlined function.
61 double scale_factor = 1.0;
62 if (caller_graph_->graph_entry()->entry_count() != 0) {
63 scale_factor =
64 static_cast<double>(call_->CallCount()) /
65 static_cast<double>(caller_graph_->graph_entry()->entry_count());
66 }
67 for (BlockIterator block_it = callee_graph->postorder_iterator();
68 !block_it.Done(); block_it.Advance()) {
69 BlockEntryInstr* block = block_it.Current();
70 if (block->IsTargetEntry()) {
71 block->AsTargetEntry()->adjust_edge_weight(scale_factor);
72 }
73 Instruction* instr = block;
74 if (block->env() != nullptr) {
75 env->DeepCopyToOuter(zone, block, outer_deopt_id);
76 }
77 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
78 instr = it.Current();
79 // TODO(zerny): Avoid creating unnecessary environments. Note that some
80 // optimizations need deoptimization info for non-deoptable instructions,
81 // eg, LICM on GOTOs.
82 if (instr->env() != nullptr) {
83 env->DeepCopyToOuter(zone, instr, outer_deopt_id);
84 }
85 }
86 if (instr->IsGoto()) {
87 instr->AsGoto()->adjust_edge_weight(scale_factor);
88 }
89 }
90
91 RemoveUnreachableExits(callee_graph);
92}
93
95 Data data = {nullptr, exit};
96 exits_.Add(data);
97}
98
100 // It doesn't make sense to combine different calls or calls from
101 // different graphs.
102 ASSERT(caller_graph_ == other->caller_graph_);
103 ASSERT(call_ == other->call_);
104 exits_.AddArray(other->exits_);
105}
106
107int InlineExitCollector::LowestBlockIdFirst(const Data* a, const Data* b) {
108 return (a->exit_block->block_id() - b->exit_block->block_id());
109}
110
111void InlineExitCollector::RemoveUnreachableExits(FlowGraph* callee_graph) {
112 const GrowableArray<BlockEntryInstr*>& postorder = callee_graph->postorder();
113 int j = 0;
114 for (int i = 0; i < exits_.length(); ++i) {
115 BlockEntryInstr* block = exits_[i].exit_return->GetBlock();
116 if ((block != nullptr) && (0 <= block->postorder_number()) &&
117 (block->postorder_number() < postorder.length()) &&
118 (postorder[block->postorder_number()] == block)) {
119 if (i != j) {
120 exits_[j] = exits_[i];
121 }
122 j++;
123 }
124 }
125 exits_.TruncateTo(j);
126}
127
128void InlineExitCollector::SortExits() {
129 // Assign block entries here because we did not necessarily know them when
130 // the return exit was added to the array.
131 for (int i = 0; i < exits_.length(); ++i) {
132 exits_[i].exit_block = exits_[i].exit_return->GetBlock();
133 }
134 exits_.Sort(LowestBlockIdFirst);
135}
136
137Definition* InlineExitCollector::JoinReturns(BlockEntryInstr** exit_block,
138 Instruction** last_instruction,
139 intptr_t try_index) {
140 // First sort the list of exits by block id (caching return instruction
141 // block entries as a side effect).
142 SortExits();
143 intptr_t num_exits = exits_.length();
144 if (num_exits == 1) {
145 ReturnAt(0)->UnuseAllInputs();
146 *exit_block = ExitBlockAt(0);
147 *last_instruction = LastInstructionAt(0);
148 return call_->HasUses() ? ValueAt(0)->definition() : nullptr;
149 } else {
150 ASSERT(num_exits > 1);
151 // Create a join of the returns.
152 intptr_t join_id = caller_graph_->max_block_id() + 1;
153 caller_graph_->set_max_block_id(join_id);
154 JoinEntryInstr* join = new (Z) JoinEntryInstr(
155 join_id, try_index, CompilerState::Current().GetNextDeoptId());
156
157 // The dominator set of the join is the intersection of the dominator
158 // sets of all the predecessors. If we keep the dominator sets ordered
159 // by height in the dominator tree, we can also get the immediate
160 // dominator of the join node from the intersection.
161 //
162 // block_dominators is the dominator set for each block, ordered from
163 // the immediate dominator to the root of the dominator tree. This is
164 // the order we collect them in (adding at the end).
165 //
166 // join_dominators is the join's dominators ordered from the root of the
167 // dominator tree to the immediate dominator. This order supports
168 // removing during intersection by truncating the list.
169 GrowableArray<BlockEntryInstr*> block_dominators;
170 GrowableArray<BlockEntryInstr*> join_dominators;
171 for (intptr_t i = 0; i < num_exits; ++i) {
172 // Add the control-flow edge.
173 GotoInstr* goto_instr =
174 new (Z) GotoInstr(join, CompilerState::Current().GetNextDeoptId());
175 goto_instr->InheritDeoptTarget(zone(), ReturnAt(i));
176 LastInstructionAt(i)->LinkTo(goto_instr);
177 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
178 join->predecessors_.Add(ExitBlockAt(i));
179
180 // Collect the block's dominators.
181 block_dominators.Clear();
182 BlockEntryInstr* dominator = ExitBlockAt(i)->dominator();
183 while (dominator != nullptr) {
184 block_dominators.Add(dominator);
185 dominator = dominator->dominator();
186 }
187
188 if (i == 0) {
189 // The initial dominator set is the first predecessor's dominator
190 // set. Reverse it.
191 for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) {
192 join_dominators.Add(block_dominators[j]);
193 }
194 } else {
195 // Intersect the block's dominators with the join's dominators so far.
196 intptr_t last = block_dominators.length() - 1;
197 for (intptr_t j = 0; j < join_dominators.length(); ++j) {
198 intptr_t k = last - j; // Corresponding index in block_dominators.
199 if ((k < 0) || (join_dominators[j] != block_dominators[k])) {
200 // We either exhausted the dominators for this block before
201 // exhausting the current intersection, or else we found a block
202 // on the path from the root of the tree that is not in common.
203 // I.e., there cannot be an empty set of dominators.
204 ASSERT(j > 0);
205 join_dominators.TruncateTo(j);
206 break;
207 }
208 }
209 }
210 }
211 // The immediate dominator of the join is the last one in the ordered
212 // intersection.
213 join_dominators.Last()->AddDominatedBlock(join);
214 *exit_block = join;
215 *last_instruction = join;
216
217 // If the call has uses, create a phi of the returns.
218 if (call_->HasUses()) {
219 // Add a phi of the return values.
220 PhiInstr* phi = new (Z) PhiInstr(join, num_exits);
221 caller_graph_->AllocateSSAIndex(phi);
222 phi->mark_alive();
223 for (intptr_t i = 0; i < num_exits; ++i) {
224 ReturnAt(i)->RemoveEnvironment();
225 phi->SetInputAt(i, ValueAt(i));
226 }
227 join->InsertPhi(phi);
228 join->InheritDeoptTargetAfter(caller_graph_, call_, phi);
229 return phi;
230 } else {
231 // In the case that the result is unused, remove the return value uses
232 // from their definition's use list.
233 for (intptr_t i = 0; i < num_exits; ++i) {
234 ReturnAt(i)->UnuseAllInputs();
235 }
236 join->InheritDeoptTargetAfter(caller_graph_, call_, nullptr);
237 return nullptr;
238 }
239 }
240}
241
243 ASSERT(call_->previous() != nullptr);
244 ASSERT(call_->next() != nullptr);
245 BlockEntryInstr* call_block = call_->GetBlock();
246
247 // Insert the callee graph into the caller graph.
248 BlockEntryInstr* callee_exit = nullptr;
249 Instruction* callee_last_instruction = nullptr;
250
251 if (exits_.length() == 0) {
252 // Handle the case when there are no normal return exits from the callee
253 // (i.e. the callee unconditionally throws) by inserting an artificial
254 // branch (true === true).
255 // The true successor is the inlined body, the false successor
256 // goes to the rest of the caller graph. It is removed as unreachable code
257 // by the constant propagation.
258 TargetEntryInstr* false_block = new (Z) TargetEntryInstr(
259 caller_graph_->allocate_block_id(), call_block->try_index(),
261 false_block->InheritDeoptTargetAfter(caller_graph_, call_, nullptr);
262 false_block->LinkTo(call_->next());
263 call_block->ReplaceAsPredecessorWith(false_block);
264
265 ConstantInstr* true_const = caller_graph_->GetConstant(Bool::True());
266 BranchInstr* branch = new (Z) BranchInstr(
267 new (Z) StrictCompareInstr(InstructionSource(), Token::kEQ_STRICT,
268 new (Z) Value(true_const),
269 new (Z) Value(true_const), false,
270 CompilerState::Current().GetNextDeoptId()),
271 CompilerState::Current().GetNextDeoptId()); // No number check.
272 branch->InheritDeoptTarget(zone(), call_);
273
274 auto true_target = BranchSimplifier::ToTargetEntry(zone(), callee_entry);
275 callee_entry->ReplaceAsPredecessorWith(true_target);
276
277 *branch->true_successor_address() = true_target;
278 *branch->false_successor_address() = false_block;
279
280 call_->previous()->AppendInstruction(branch);
281 call_block->set_last_instruction(branch);
282
283 // Replace uses of the return value with sentinel constant to maintain
284 // valid SSA form - even though the rest of the caller is unreachable.
285 call_->ReplaceUsesWith(caller_graph_->GetConstant(Object::sentinel()));
286
287 // Update dominator tree.
288 for (intptr_t i = 0, n = callee_entry->dominated_blocks().length(); i < n;
289 i++) {
290 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
291 true_target->AddDominatedBlock(block);
292 }
293 for (intptr_t i = 0, n = call_block->dominated_blocks().length(); i < n;
294 i++) {
295 BlockEntryInstr* block = call_block->dominated_blocks()[i];
296 false_block->AddDominatedBlock(block);
297 }
298 call_block->ClearDominatedBlocks();
299 call_block->AddDominatedBlock(true_target);
300 call_block->AddDominatedBlock(false_block);
301
302 } else {
303 Definition* callee_result = JoinReturns(
304 &callee_exit, &callee_last_instruction, call_block->try_index());
305 if (callee_result != nullptr) {
306 call_->ReplaceUsesWith(callee_result);
307 }
308 if (callee_last_instruction == callee_entry) {
309 // There are no instructions in the inlined function (e.g., it might be
310 // a return of a parameter or a return of a constant defined in the
311 // initial definitions).
312 call_->previous()->LinkTo(call_->next());
313 } else {
314 call_->previous()->LinkTo(callee_entry->next());
315 callee_last_instruction->LinkTo(call_->next());
316 }
317 if (callee_exit != callee_entry) {
318 // In case of control flow, locally update the predecessors, phis and
319 // dominator tree.
320 //
321 // Pictorially, the graph structure is:
322 //
323 // Bc : call_block Bi : callee_entry
324 // before_call inlined_head
325 // call ... other blocks ...
326 // after_call Be : callee_exit
327 // inlined_foot
328 // And becomes:
329 //
330 // Bc : call_block
331 // before_call
332 // inlined_head
333 // ... other blocks ...
334 // Be : callee_exit
335 // inlined_foot
336 // after_call
337 //
338 // For successors of 'after_call', the call block (Bc) is replaced as a
339 // predecessor by the callee exit (Be).
340 call_block->ReplaceAsPredecessorWith(callee_exit);
341 // For successors of 'inlined_head', the callee entry (Bi) is replaced
342 // as a predecessor by the call block (Bc).
343 callee_entry->ReplaceAsPredecessorWith(call_block);
344
345 // The callee exit is now the immediate dominator of blocks whose
346 // immediate dominator was the call block.
347 ASSERT(callee_exit->dominated_blocks().is_empty());
348 for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) {
349 BlockEntryInstr* block = call_block->dominated_blocks()[i];
350 callee_exit->AddDominatedBlock(block);
351 }
352 // The call block is now the immediate dominator of blocks whose
353 // immediate dominator was the callee entry.
354 call_block->ClearDominatedBlocks();
355 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
356 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
357 call_block->AddDominatedBlock(block);
358 }
359 }
360
361 // Callee entry in not in the graph anymore. Remove it from use lists.
362 callee_entry->UnuseAllInputs();
363 }
364 // Neither call nor the graph entry (if present) are in the
365 // graph at this point. Remove them from use lists.
366 if (callee_entry->PredecessorCount() > 0) {
367 callee_entry->PredecessorAt(0)->AsGraphEntry()->UnuseAllInputs();
368 }
369 call_->UnuseAllInputs();
370}
371
373 // Bail if the type is still uninstantiated at compile time.
374 if (!type.IsInstantiated()) return false;
375
376 // Bail if the type is a function, record or a Dart Function type.
377 if (type.IsFunctionType() || type.IsRecordType() ||
378 type.IsDartFunctionType()) {
379 return false;
380 }
381
382 ASSERT(type.HasTypeClass());
383 auto* const zone = Thread::Current()->zone();
384 const Class& type_class = Class::Handle(zone, type.type_class());
385
386 // Bail if the type has any type parameters.
387 if (type_class.IsGeneric()) {
388 // If the interface type is a supertype of the rare type, as any instances
389 // are guaranteed to be subtypes of the rare type, then we can still use
390 // the _simpleInstanceOf implementation (see also
391 // runtime/lib/object.cc:Object_SimpleInstanceOf).
392 // See #52848 for why the type might be a supertype and not strictly equal.
393 const auto& rare_type = Type::Handle(zone, type_class.RareType());
394 return rare_type.IsSubtypeOf(type, Heap::kNew);
395 }
396
397 // Finally a simple class for instance of checking.
398 return true;
399}
400
401} // namespace dart
static float next(float f)
GLenum type
void TruncateTo(intptr_t length)
void AddArray(const BaseGrowableArray< T, B, Allocator > &src)
void Add(const T &value)
void Sort(int compare(const T *, const T *))
intptr_t length() const
BlockEntryInstr * dominator() const
Definition: il.h:1670
void ClearDominatedBlocks()
Definition: il.h:1682
intptr_t try_index() const
Definition: il.h:1730
void AddDominatedBlock(BlockEntryInstr *block)
Definition: il.h:1677
virtual intptr_t PredecessorCount() const =0
const GrowableArray< BlockEntryInstr * > & dominated_blocks()
Definition: il.h:1673
void ReplaceAsPredecessorWith(BlockEntryInstr *new_block)
Definition: il.cc:1847
virtual BlockEntryInstr * PredecessorAt(intptr_t index) const =0
void set_last_instruction(Instruction *instr)
Definition: il.h:1687
bool Done() const
Definition: flow_graph.h:46
static const Bool & True()
Definition: object.h:10797
TargetEntryInstr ** false_successor_address()
Definition: il.h:4051
TargetEntryInstr ** true_successor_address()
Definition: il.h:4050
static TargetEntryInstr * ToTargetEntry(Zone *zone, BlockEntryInstr *target)
TypePtr RareType() const
Definition: object.cc:3036
bool IsGeneric() const
Definition: object.h:1358
intptr_t GetNextDeoptId()
static CompilerState & Current()
bool HasUses() const
Definition: il.h:2569
virtual intptr_t CallCount() const
Definition: il.h:2496
void ReplaceUsesWith(Definition *other)
Definition: il.cc:1493
static constexpr intptr_t kNone
Definition: deopt_id.h:27
GraphEntryInstr * graph_entry() const
Definition: flow_graph.h:268
ConstantInstr * GetConstant(const Object &object, Representation representation=kTagged)
Definition: flow_graph.cc:187
Zone * zone() const
Definition: flow_graph.h:261
intptr_t current_ssa_temp_index() const
Definition: flow_graph.h:243
Thread * thread() const
Definition: flow_graph.h:260
void set_current_ssa_temp_index(intptr_t index)
Definition: flow_graph.h:244
BlockIterator postorder_iterator() const
Definition: flow_graph.h:222
intptr_t max_block_id() const
Definition: flow_graph.h:264
void set_max_block_id(intptr_t id)
Definition: flow_graph.h:265
void AllocateSSAIndex(Definition *def)
Definition: flow_graph.h:274
intptr_t allocate_block_id()
Definition: flow_graph.h:266
intptr_t entry_count() const
Definition: il.h:1980
virtual intptr_t SuccessorCount() const
Definition: il.cc:1988
@ kNew
Definition: heap.h:38
void Union(const InlineExitCollector *other)
void PrepareGraphs(FlowGraph *callee_graph)
void AddExit(DartReturnInstr *exit)
void ReplaceCall(BlockEntryInstr *callee_entry)
Instruction * next() const
Definition: il.h:1093
void InheritDeoptTargetAfter(FlowGraph *flow_graph, Definition *call, Definition *result)
Definition: il.cc:1558
void LinkTo(Instruction *next)
Definition: il.h:1108
void InheritDeoptTarget(Zone *zone, Instruction *other)
Definition: il.cc:1569
virtual BlockEntryInstr * GetBlock()
Definition: il.cc:1352
Environment * env() const
Definition: il.h:1215
void RemoveEnvironment()
Definition: il.cc:1282
Instruction * AppendInstruction(Instruction *tail)
Definition: il.cc:1341
void UnuseAllInputs()
Definition: il.cc:1534
intptr_t deopt_id() const
Definition: il.h:993
Instruction * previous() const
Definition: il.h:1087
static Object & Handle()
Definition: object.h:407
Zone * zone() const
Definition: thread_state.h:37
static Thread * Current()
Definition: thread.h:362
static bool DoublesBitEqual(const double a, const double b)
Definition: utils.h:525
Definition: il.h:75
Definition * definition() const
Definition: il.h:103
#define COMPILER_TIMINGS_TIMER_SCOPE(thread, timer_id)
#define ASSERT(E)
static bool b
struct MyStruct a[10]
uint8_t value
#define Z
size_t length
exit(kErrorExitCode)
Definition: dart_vm.cc:33
uword FindDoubleConstant(double value)
bool SimpleInstanceOfType(const AbstractType &type)
uintptr_t uword
Definition: globals.h:501
const double kCommonDoubleConstants[]
static int8_t data[kExtLength]
Definition: __init__.py:1
struct PathData * Data(SkPath *path)
Definition: path_ops.cc:52
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741