Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
block_scheduler.cc
Go to the documentation of this file.
1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
6
7#include "vm/allocation.h"
8#include "vm/code_patcher.h"
11
12namespace dart {
13
14static intptr_t GetEdgeCount(const Array& edge_counters, intptr_t edge_id) {
15 if (!FLAG_reorder_basic_blocks) {
16 // Assume everything was visited once.
17 return 1;
18 }
19 return Smi::Value(Smi::RawCast(edge_counters.At(edge_id)));
20}
21
22// There is an edge from instruction->successor. Set its weight (edge count
23// per function entry).
24static void SetEdgeWeight(BlockEntryInstr* block,
25 BlockEntryInstr* successor,
26 const Array& edge_counters,
27 intptr_t entry_count) {
28 ASSERT(entry_count != 0);
29 if (auto target = successor->AsTargetEntry()) {
30 // If this block ends in a goto, the edge count of this edge is the same
31 // as the count on the single outgoing edge. This is true as long as the
32 // block does not throw an exception.
33 intptr_t count = GetEdgeCount(edge_counters, target->preorder_number());
34 if (count >= 0) {
35 double weight =
36 static_cast<double>(count) / static_cast<double>(entry_count);
37 target->set_edge_weight(weight);
38 }
39 } else if (auto jump = block->last_instruction()->AsGoto()) {
40 intptr_t count = GetEdgeCount(edge_counters, block->preorder_number());
41 if (count >= 0) {
42 double weight =
43 static_cast<double>(count) / static_cast<double>(entry_count);
44 jump->set_edge_weight(weight);
45 }
46 }
47}
48
50 if (!FLAG_reorder_basic_blocks) {
51 return;
52 }
53 if (CompilerState::Current().is_aot()) {
54 return;
55 }
56
57 const Function& function = flow_graph->parsed_function().function();
58 const Array& ic_data_array =
59 Array::Handle(flow_graph->zone(), function.ic_data_array());
60 if (ic_data_array.IsNull()) {
61 DEBUG_ASSERT(IsolateGroup::Current()->HasAttemptedReload() ||
62 function.ForceOptimize());
63 return;
64 }
65 Array& edge_counters = Array::Handle();
66 edge_counters ^=
68 if (edge_counters.IsNull()) {
69 return;
70 }
71
72 auto graph_entry = flow_graph->graph_entry();
73 BlockEntryInstr* entry = graph_entry->normal_entry();
74 if (entry == nullptr) {
75 entry = graph_entry->osr_entry();
76 ASSERT(entry != nullptr);
77 }
78 const intptr_t entry_count =
79 GetEdgeCount(edge_counters, entry->preorder_number());
80 graph_entry->set_entry_count(entry_count);
81 if (entry_count == 0) {
82 return; // Nothing to do.
83 }
84
85 for (BlockIterator it = flow_graph->reverse_postorder_iterator(); !it.Done();
86 it.Advance()) {
87 BlockEntryInstr* block = it.Current();
88 Instruction* last = block->last_instruction();
89 for (intptr_t i = 0; i < last->SuccessorCount(); ++i) {
90 BlockEntryInstr* succ = last->SuccessorAt(i);
91 SetEdgeWeight(block, succ, edge_counters, entry_count);
92 }
93 }
94}
95
96// A weighted control-flow graph edge.
107
108// A linked list node in a chain of blocks.
115
116// A chain of blocks with first and last pointers for fast concatenation and
117// a length to support adding a shorter chain's links to a longer chain.
118struct Chain : public ZoneAllocated {
119 explicit Chain(BlockEntryInstr* block)
120 : first(new Link(block, nullptr)), last(first), length(1) {}
121
124 intptr_t length;
125};
126
127int Edge::LowestWeightFirst(const Edge* a, const Edge* b) {
128 if (a->weight < b->weight) {
129 return -1;
130 }
131 return (a->weight > b->weight) ? 1 : 0;
132}
133
134// Combine two chains by adding the shorter chain's links to the longer
135// chain.
136static void Union(GrowableArray<Chain*>* chains,
137 Chain* source_chain,
138 Chain* target_chain) {
139 if (source_chain->length < target_chain->length) {
140 for (Link* link = source_chain->first; link != nullptr; link = link->next) {
141 (*chains)[link->block->postorder_number()] = target_chain;
142 }
143 // Link the chains.
144 source_chain->last->next = target_chain->first;
145 // Update the state of the longer chain.
146 target_chain->first = source_chain->first;
147 target_chain->length += source_chain->length;
148 } else {
149 for (Link* link = target_chain->first; link != nullptr; link = link->next) {
150 (*chains)[link->block->postorder_number()] = source_chain;
151 }
152 source_chain->last->next = target_chain->first;
153 source_chain->last = target_chain->last;
154 source_chain->length += target_chain->length;
155 }
156}
157
159 if (!flow_graph->should_reorder_blocks()) {
160 return;
161 }
162
163 if (CompilerState::Current().is_aot()) {
164 ReorderBlocksAOT(flow_graph);
165 } else {
166 ReorderBlocksJIT(flow_graph);
167 }
168}
169
170void BlockScheduler::ReorderBlocksJIT(FlowGraph* flow_graph) {
171 // Add every block to a chain of length 1 and compute a list of edges
172 // sorted by weight.
173 intptr_t block_count = flow_graph->preorder().length();
175
176 // A map from a block's postorder number to the chain it is in. Used to
177 // implement a simple (ordered) union-find data structure. Chains are
178 // stored by pointer so that they are aliased (mutating one mutates all
179 // shared ones). Find(n) is simply chains[n].
181
182 for (BlockIterator it = flow_graph->postorder_iterator(); !it.Done();
183 it.Advance()) {
184 BlockEntryInstr* block = it.Current();
185 chains.Add(new Chain(block));
186
187 Instruction* last = block->last_instruction();
188 for (intptr_t i = 0; i < last->SuccessorCount(); ++i) {
189 BlockEntryInstr* succ = last->SuccessorAt(i);
190 double weight = 0.0;
191 if (succ->IsTargetEntry()) {
192 weight = succ->AsTargetEntry()->edge_weight();
193 } else if (last->IsGoto()) {
194 weight = last->AsGoto()->edge_weight();
195 }
196 edges.Add(Edge(block, succ, weight));
197 }
198 }
199
200 // Handle each edge in turn. The edges are sorted by increasing weight.
201 edges.Sort(Edge::LowestWeightFirst);
202 while (!edges.is_empty()) {
203 Edge edge = edges.RemoveLast();
204 Chain* source_chain = chains[edge.source->postorder_number()];
205 Chain* target_chain = chains[edge.target->postorder_number()];
206
207 // If the source and target are already in the same chain or if the
208 // edge's source or target is not exposed at the appropriate end of a
209 // chain skip this edge.
210 if ((source_chain == target_chain) ||
211 (edge.source != source_chain->last->block) ||
212 (edge.target != target_chain->first->block)) {
213 continue;
214 }
215
216 Union(&chains, source_chain, target_chain);
217 }
218
219 // Ensure the checked entry remains first to avoid needing another offset on
220 // Instructions, compare Code::EntryPointOf.
221 GraphEntryInstr* graph_entry = flow_graph->graph_entry();
222 flow_graph->CodegenBlockOrder()->Add(graph_entry);
223 FunctionEntryInstr* checked_entry = graph_entry->normal_entry();
224 if (checked_entry != nullptr) {
225 flow_graph->CodegenBlockOrder()->Add(checked_entry);
226 }
227 // Build a new block order. Emit each chain when its first block occurs
228 // in the original reverse postorder ordering.
229 // Note: the resulting order is not topologically sorted and can't be
230 // used a replacement for reverse_postorder in algorithms that expect
231 // topological sort.
232 for (intptr_t i = block_count - 1; i >= 0; --i) {
233 if (chains[i]->first->block == flow_graph->postorder()[i]) {
234 for (Link* link = chains[i]->first; link != nullptr; link = link->next) {
235 if ((link->block != checked_entry) && (link->block != graph_entry)) {
236 flow_graph->CodegenBlockOrder()->Add(link->block);
237 }
238 }
239 }
240 }
241}
242
243// AOT block order is based on reverse post order but with two changes:
244//
245// - Blocks which always throw and their direct predecessors are considered
246// *cold* and moved to the end of the order.
247// - Blocks which belong to the same loop are kept together (where possible)
248// and not interspersed with other blocks.
249//
250namespace {
251class AOTBlockScheduler {
252 public:
253 explicit AOTBlockScheduler(FlowGraph* flow_graph)
254 : flow_graph_(flow_graph),
255 block_count_(flow_graph->reverse_postorder().length()),
256 marks_(block_count_),
257 postorder_(block_count_),
258 cold_postorder_(10) {
259 marks_.FillWith(0, 0, block_count_);
260 }
261
262 void ComputeOrder() {
263 ComputeOrderImpl();
264
265 const auto codegen_order = flow_graph_->CodegenBlockOrder();
266 for (intptr_t i = postorder_.length() - 1; i >= 0; --i) {
267 codegen_order->Add(postorder_[i]);
268 }
269 for (intptr_t i = cold_postorder_.length() - 1; i >= 0; --i) {
270 codegen_order->Add(cold_postorder_[i]);
271 }
272 }
273
274 private:
275 // The algorithm below is almost identical to |FlowGraph::DiscoverBlocks|, but
276 // with few tweaks which guarantee improved scheduling for cold code and
277 // loops.
278 void ComputeOrderImpl() {
279 PushBlock(flow_graph_->graph_entry());
280 while (!block_stack_.is_empty()) {
281 BlockEntryInstr* block = block_stack_.Last();
282 auto& marks = MarksOf(block);
283 auto last = block->last_instruction();
284 const auto successor_count = last->SuccessorCount();
285
286 if ((marks & kVisitedMark) == 0) {
287 marks |= kVisitedMark;
288
289 if (last->IsThrow() || last->IsReThrow()) {
290 marks |= kColdMark;
291 } else {
292 // When visiting a block inside a loop with two successors
293 // push the successor with lesser nesting *last*, so that it is
294 // visited first. This helps to keep blocks which belong to the
295 // same loop together.
296 //
297 // This is the main difference from |DiscoverBlocks| which always
298 // visits successors in reverse order.
299 if (successor_count == 2 && block->loop_info() != nullptr) {
300 auto succ0 = last->SuccessorAt(0);
301 auto succ1 = last->SuccessorAt(1);
302
303 if (succ0->NestingDepth() < succ1->NestingDepth()) {
304 PushBlock(succ1);
305 PushBlock(succ0);
306 } else {
307 PushBlock(succ0);
308 PushBlock(succ1);
309 }
310 } else {
311 for (intptr_t i = 0; i < successor_count; i++) {
312 PushBlock(last->SuccessorAt(i));
313 }
314 }
315
316 // We have pushed some successors to the stack. Process them first.
317 if (block_stack_.Last() != block) {
318 continue;
319 }
320
321 // No successors added, fall through.
322 }
323 }
324
325 // All successors of this block were visited, which means we are
326 // done with this block.
327 block_stack_.RemoveLast();
328
329 // Propagate cold mark from the successors: if all successors are
330 // cold then this block is cold as well.
331 if (successor_count > 0) {
332 uint8_t cold_mark = kColdMark;
333 for (intptr_t i = 0; i < successor_count; i++) {
334 cold_mark &= MarksOf(last->SuccessorAt(i));
335 }
336 marks |= cold_mark;
337 }
338
339 if ((marks & (kColdMark | kPinnedMark)) == kColdMark) {
340 // This block is cold and not pinned: move it to cold section at
341 // the end.
342 cold_postorder_.Add(block);
343 } else {
344 postorder_.Add(block);
345 }
346 }
347 }
348
349 // The block was added to the stack.
350 static constexpr uint8_t kSeenMark = 1 << 0;
351 // The block was visited and all of its successors were added to the stack.
352 static constexpr uint8_t kVisitedMark = 1 << 1;
353 // The block terminates with unconditional throw or rethrow.
354 static constexpr uint8_t kColdMark = 1 << 2;
355 // The block should not move to cold section.
356 static constexpr uint8_t kPinnedMark = 1 << 3;
357
358 uint8_t& MarksOf(BlockEntryInstr* block) {
359 return marks_[block->preorder_number()];
360 }
361
362 void PushBlock(BlockEntryInstr* block) {
363 auto& marks = MarksOf(block);
364 if ((marks & kSeenMark) == 0) {
365 marks |= kSeenMark;
366 block_stack_.Add(block);
367
368 if (block->IsFunctionEntry() || block->IsGraphEntry()) {
369 marks |= kPinnedMark;
370 }
371 }
372 }
373
374 FlowGraph* const flow_graph_;
375 const intptr_t block_count_;
376
377 // Block marks for each block indexed by block preorder number.
378 GrowableArray<uint8_t> marks_;
379
380 // Stack of blocks to process.
381 GrowableArray<BlockEntryInstr*> block_stack_;
382
383 GrowableArray<BlockEntryInstr*> postorder_;
384 GrowableArray<BlockEntryInstr*> cold_postorder_;
385};
386} // namespace
387
388void BlockScheduler::ReorderBlocksAOT(FlowGraph* flow_graph) {
389 AOTBlockScheduler(flow_graph).ComputeOrder();
390}
391
392} // namespace dart
int count
static int block_count(const SkSBlockAllocator< N > &pool)
#define DEBUG_ASSERT(cond)
Definition assert.h:321
ObjectPtr At(intptr_t index) const
Definition object.h:10854
intptr_t preorder_number() const
Definition il.h:1649
Instruction * last_instruction() const
Definition il.h:1680
static void AssignEdgeWeights(FlowGraph *flow_graph)
static void ReorderBlocks(FlowGraph *flow_graph)
static CompilerState & Current()
GraphEntryInstr * graph_entry() const
Definition flow_graph.h:268
Zone * zone() const
Definition flow_graph.h:261
const GrowableArray< BlockEntryInstr * > & preorder() const
Definition flow_graph.h:203
BlockIterator postorder_iterator() const
Definition flow_graph.h:222
const GrowableArray< BlockEntryInstr * > & postorder() const
Definition flow_graph.h:204
const ParsedFunction & parsed_function() const
Definition flow_graph.h:129
BlockIterator reverse_postorder_iterator() const
Definition flow_graph.h:219
GrowableArray< BlockEntryInstr * > * CodegenBlockOrder()
bool should_reorder_blocks() const
Definition flow_graph.h:510
virtual BlockEntryInstr * SuccessorAt(intptr_t index) const
Definition il.cc:1972
virtual intptr_t SuccessorCount() const
Definition il.cc:1968
static IsolateGroup * Current()
Definition isolate.h:534
bool IsNull() const
Definition object.h:363
static Object & Handle()
Definition object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition object.h:325
const Function & function() const
Definition parser.h:73
intptr_t Value() const
Definition object.h:9969
#define ASSERT(E)
static bool b
struct MyStruct a[10]
uint32_t * target
Dart_NativeFunction function
Definition fuchsia.cc:51
size_t length
link(from_root, to_root)
Definition dart_pkg.py:44
static void SetEdgeWeight(BlockEntryInstr *block, BlockEntryInstr *successor, const Array &edge_counters, intptr_t entry_count)
static void Union(GrowableArray< Chain * > *chains, Chain *source_chain, Chain *target_chain)
static intptr_t GetEdgeCount(const Array &edge_counters, intptr_t edge_id)
Chain(BlockEntryInstr *block)
BlockEntryInstr * target
Edge(BlockEntryInstr *source, BlockEntryInstr *target, double weight)
BlockEntryInstr * source
static int LowestWeightFirst(const Edge *a, const Edge *b)
static constexpr intptr_t kEdgeCounters
Definition object.h:4039