|  | // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/compiler/backend/block_scheduler.h" | 
|  |  | 
|  | #include "vm/allocation.h" | 
|  | #include "vm/code_patcher.h" | 
|  | #include "vm/compiler/backend/flow_graph.h" | 
|  | #include "vm/compiler/jit/compiler.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | static intptr_t GetEdgeCount(const Array& edge_counters, intptr_t edge_id) { | 
|  | if (!FLAG_reorder_basic_blocks) { | 
|  | // Assume everything was visited once. | 
|  | return 1; | 
|  | } | 
|  | return Smi::Value(Smi::RawCast(edge_counters.At(edge_id))); | 
|  | } | 
|  |  | 
|  | // There is an edge from instruction->successor.  Set its weight (edge count | 
|  | // per function entry). | 
|  | static void SetEdgeWeight(BlockEntryInstr* block, | 
|  | BlockEntryInstr* successor, | 
|  | const Array& edge_counters, | 
|  | intptr_t entry_count) { | 
|  | ASSERT(entry_count != 0); | 
|  | if (auto target = successor->AsTargetEntry()) { | 
|  | // If this block ends in a goto, the edge count of this edge is the same | 
|  | // as the count on the single outgoing edge. This is true as long as the | 
|  | // block does not throw an exception. | 
|  | intptr_t count = GetEdgeCount(edge_counters, target->preorder_number()); | 
|  | if (count >= 0) { | 
|  | double weight = | 
|  | static_cast<double>(count) / static_cast<double>(entry_count); | 
|  | target->set_edge_weight(weight); | 
|  | } | 
|  | } else if (auto jump = block->last_instruction()->AsGoto()) { | 
|  | intptr_t count = GetEdgeCount(edge_counters, block->preorder_number()); | 
|  | if (count >= 0) { | 
|  | double weight = | 
|  | static_cast<double>(count) / static_cast<double>(entry_count); | 
|  | jump->set_edge_weight(weight); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void BlockScheduler::AssignEdgeWeights(FlowGraph* flow_graph) { | 
|  | if (!FLAG_reorder_basic_blocks) { | 
|  | return; | 
|  | } | 
|  | if (CompilerState::Current().is_aot()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const Function& function = flow_graph->parsed_function().function(); | 
|  | const Array& ic_data_array = | 
|  | Array::Handle(flow_graph->zone(), function.ic_data_array()); | 
|  | if (ic_data_array.IsNull()) { | 
|  | DEBUG_ASSERT(IsolateGroup::Current()->HasAttemptedReload() || | 
|  | function.ForceOptimize()); | 
|  | return; | 
|  | } | 
|  | Array& edge_counters = Array::Handle(); | 
|  | edge_counters ^= | 
|  | ic_data_array.At(Function::ICDataArrayIndices::kEdgeCounters); | 
|  | if (edge_counters.IsNull()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto graph_entry = flow_graph->graph_entry(); | 
|  | BlockEntryInstr* entry = graph_entry->normal_entry(); | 
|  | if (entry == nullptr) { | 
|  | entry = graph_entry->osr_entry(); | 
|  | ASSERT(entry != nullptr); | 
|  | } | 
|  | const intptr_t entry_count = | 
|  | GetEdgeCount(edge_counters, entry->preorder_number()); | 
|  | graph_entry->set_entry_count(entry_count); | 
|  | if (entry_count == 0) { | 
|  | return;  // Nothing to do. | 
|  | } | 
|  |  | 
|  | for (BlockIterator it = flow_graph->reverse_postorder_iterator(); !it.Done(); | 
|  | it.Advance()) { | 
|  | BlockEntryInstr* block = it.Current(); | 
|  | Instruction* last = block->last_instruction(); | 
|  | for (intptr_t i = 0; i < last->SuccessorCount(); ++i) { | 
|  | BlockEntryInstr* succ = last->SuccessorAt(i); | 
|  | SetEdgeWeight(block, succ, edge_counters, entry_count); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // A weighted control-flow graph edge. | 
|  | struct Edge { | 
|  | Edge(BlockEntryInstr* source, BlockEntryInstr* target, double weight) | 
|  | : source(source), target(target), weight(weight) {} | 
|  |  | 
|  | static int LowestWeightFirst(const Edge* a, const Edge* b); | 
|  |  | 
|  | BlockEntryInstr* source; | 
|  | BlockEntryInstr* target; | 
|  | double weight; | 
|  | }; | 
|  |  | 
|  | // A linked list node in a chain of blocks. | 
|  | struct Link : public ZoneAllocated { | 
|  | Link(BlockEntryInstr* block, Link* next) : block(block), next(next) {} | 
|  |  | 
|  | BlockEntryInstr* block; | 
|  | Link* next; | 
|  | }; | 
|  |  | 
|  | // A chain of blocks with first and last pointers for fast concatenation and | 
|  | // a length to support adding a shorter chain's links to a longer chain. | 
|  | struct Chain : public ZoneAllocated { | 
|  | explicit Chain(BlockEntryInstr* block) | 
|  | : first(new Link(block, nullptr)), last(first), length(1) {} | 
|  |  | 
|  | Link* first; | 
|  | Link* last; | 
|  | intptr_t length; | 
|  | }; | 
|  |  | 
|  | int Edge::LowestWeightFirst(const Edge* a, const Edge* b) { | 
|  | if (a->weight < b->weight) { | 
|  | return -1; | 
|  | } | 
|  | return (a->weight > b->weight) ? 1 : 0; | 
|  | } | 
|  |  | 
|  | // Combine two chains by adding the shorter chain's links to the longer | 
|  | // chain. | 
|  | static void Union(GrowableArray<Chain*>* chains, | 
|  | Chain* source_chain, | 
|  | Chain* target_chain) { | 
|  | if (source_chain->length < target_chain->length) { | 
|  | for (Link* link = source_chain->first; link != nullptr; link = link->next) { | 
|  | (*chains)[link->block->postorder_number()] = target_chain; | 
|  | } | 
|  | // Link the chains. | 
|  | source_chain->last->next = target_chain->first; | 
|  | // Update the state of the longer chain. | 
|  | target_chain->first = source_chain->first; | 
|  | target_chain->length += source_chain->length; | 
|  | } else { | 
|  | for (Link* link = target_chain->first; link != nullptr; link = link->next) { | 
|  | (*chains)[link->block->postorder_number()] = source_chain; | 
|  | } | 
|  | source_chain->last->next = target_chain->first; | 
|  | source_chain->last = target_chain->last; | 
|  | source_chain->length += target_chain->length; | 
|  | } | 
|  | } | 
|  |  | 
|  | void BlockScheduler::ReorderBlocks(FlowGraph* flow_graph) { | 
|  | if (CompilerState::Current().is_aot()) { | 
|  | ReorderBlocksAOT(flow_graph); | 
|  | } else { | 
|  | ReorderBlocksJIT(flow_graph); | 
|  | } | 
|  | } | 
|  |  | 
|  | void BlockScheduler::ReorderBlocksJIT(FlowGraph* flow_graph) { | 
|  | if (!FLAG_reorder_basic_blocks) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Add every block to a chain of length 1 and compute a list of edges | 
|  | // sorted by weight. | 
|  | intptr_t block_count = flow_graph->preorder().length(); | 
|  | GrowableArray<Edge> edges(2 * block_count); | 
|  |  | 
|  | // A map from a block's postorder number to the chain it is in.  Used to | 
|  | // implement a simple (ordered) union-find data structure.  Chains are | 
|  | // stored by pointer so that they are aliased (mutating one mutates all | 
|  | // shared ones).  Find(n) is simply chains[n]. | 
|  | GrowableArray<Chain*> chains(block_count); | 
|  |  | 
|  | for (BlockIterator it = flow_graph->postorder_iterator(); !it.Done(); | 
|  | it.Advance()) { | 
|  | BlockEntryInstr* block = it.Current(); | 
|  | chains.Add(new Chain(block)); | 
|  |  | 
|  | Instruction* last = block->last_instruction(); | 
|  | for (intptr_t i = 0; i < last->SuccessorCount(); ++i) { | 
|  | BlockEntryInstr* succ = last->SuccessorAt(i); | 
|  | double weight = 0.0; | 
|  | if (succ->IsTargetEntry()) { | 
|  | weight = succ->AsTargetEntry()->edge_weight(); | 
|  | } else if (last->IsGoto()) { | 
|  | weight = last->AsGoto()->edge_weight(); | 
|  | } | 
|  | edges.Add(Edge(block, succ, weight)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Handle each edge in turn.  The edges are sorted by increasing weight. | 
|  | edges.Sort(Edge::LowestWeightFirst); | 
|  | while (!edges.is_empty()) { | 
|  | Edge edge = edges.RemoveLast(); | 
|  | Chain* source_chain = chains[edge.source->postorder_number()]; | 
|  | Chain* target_chain = chains[edge.target->postorder_number()]; | 
|  |  | 
|  | // If the source and target are already in the same chain or if the | 
|  | // edge's source or target is not exposed at the appropriate end of a | 
|  | // chain skip this edge. | 
|  | if ((source_chain == target_chain) || | 
|  | (edge.source != source_chain->last->block) || | 
|  | (edge.target != target_chain->first->block)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Union(&chains, source_chain, target_chain); | 
|  | } | 
|  |  | 
|  | // Ensure the checked entry remains first to avoid needing another offset on | 
|  | // Instructions, compare Code::EntryPointOf. | 
|  | GraphEntryInstr* graph_entry = flow_graph->graph_entry(); | 
|  | flow_graph->CodegenBlockOrder(true)->Add(graph_entry); | 
|  | FunctionEntryInstr* checked_entry = graph_entry->normal_entry(); | 
|  | if (checked_entry != nullptr) { | 
|  | flow_graph->CodegenBlockOrder(true)->Add(checked_entry); | 
|  | } | 
|  | // Build a new block order.  Emit each chain when its first block occurs | 
|  | // in the original reverse postorder ordering (which gives a topological | 
|  | // sort of the blocks). | 
|  | for (intptr_t i = block_count - 1; i >= 0; --i) { | 
|  | if (chains[i]->first->block == flow_graph->postorder()[i]) { | 
|  | for (Link* link = chains[i]->first; link != nullptr; link = link->next) { | 
|  | if ((link->block != checked_entry) && (link->block != graph_entry)) { | 
|  | flow_graph->CodegenBlockOrder(true)->Add(link->block); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Moves blocks ending in a throw/rethrow, as well as any block post-dominated | 
|  | // by such a throwing block, to the end. | 
|  | void BlockScheduler::ReorderBlocksAOT(FlowGraph* flow_graph) { | 
|  | if (!FLAG_reorder_basic_blocks) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto& reverse_postorder = flow_graph->reverse_postorder(); | 
|  | const intptr_t block_count = reverse_postorder.length(); | 
|  | GrowableArray<bool> is_terminating(block_count); | 
|  | is_terminating.FillWith(false, 0, block_count); | 
|  |  | 
|  | // Any block in the worklist is marked and any of its unconditional | 
|  | // predecessors need to be marked as well. | 
|  | GrowableArray<BlockEntryInstr*> worklist; | 
|  |  | 
|  | // Add all throwing blocks to the worklist. | 
|  | for (intptr_t i = 0; i < block_count; ++i) { | 
|  | auto block = reverse_postorder[i]; | 
|  | auto last = block->last_instruction(); | 
|  | if (last->IsThrow() || last->IsReThrow()) { | 
|  | const intptr_t preorder_nr = block->preorder_number(); | 
|  | is_terminating[preorder_nr] = true; | 
|  | worklist.Add(block); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Follow all indirect predecessors which unconditionally will end up in a | 
|  | // throwing block. | 
|  | while (worklist.length() > 0) { | 
|  | auto block = worklist.RemoveLast(); | 
|  | for (intptr_t i = 0; i < block->PredecessorCount(); ++i) { | 
|  | auto predecessor = block->PredecessorAt(i); | 
|  | if (predecessor->last_instruction()->IsGoto()) { | 
|  | const intptr_t preorder_nr = predecessor->preorder_number(); | 
|  | if (!is_terminating[preorder_nr]) { | 
|  | is_terminating[preorder_nr] = true; | 
|  | worklist.Add(predecessor); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit code in reverse postorder but move any throwing blocks (except the | 
|  | // function entry, which needs to come first) to the very end. | 
|  | auto codegen_order = flow_graph->CodegenBlockOrder(true); | 
|  | for (intptr_t i = 0; i < block_count; ++i) { | 
|  | auto block = reverse_postorder[i]; | 
|  | const intptr_t preorder_nr = block->preorder_number(); | 
|  | if (!is_terminating[preorder_nr] || block->IsFunctionEntry()) { | 
|  | codegen_order->Add(block); | 
|  | } | 
|  | } | 
|  | for (intptr_t i = 0; i < block_count; ++i) { | 
|  | auto block = reverse_postorder[i]; | 
|  | const intptr_t preorder_nr = block->preorder_number(); | 
|  | if (is_terminating[preorder_nr] && !block->IsFunctionEntry()) { | 
|  | codegen_order->Add(block); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace dart |