4a653d9e17
Reserve first element in the Function's ic_data_array to hold the edge counter array. Until now we had a one-element array per edge counter. This reduces memory used by edge counters. This CL allows to optimize code without having to have the unoptimized code present. Also, save space in Instruction by making place_id_ and lifetime_position_ a union. place_id_ is exclusively needed by Load/StoreOptimizer, lifetime_position by the FlowGraphAllocator. BUG= R=asiva@google.com Review URL: https://codereview.chromium.org//1343383003 .
214 lines
7.0 KiB
C++
214 lines
7.0 KiB
C++
// 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/block_scheduler.h"
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/code_patcher.h"
|
|
#include "vm/flow_graph.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, emit_edge_counters, true, "Emit edge counters at targets.");
|
|
|
|
static intptr_t GetEdgeCount(const Array& edge_counters, intptr_t edge_id) {
|
|
if (!FLAG_emit_edge_counters) {
|
|
// 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) {
|
|
TargetEntryInstr* target = successor->AsTargetEntry();
|
|
if (target != NULL) {
|
|
// 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) && (entry_count != 0)) {
|
|
double weight =
|
|
static_cast<double>(count) / static_cast<double>(entry_count);
|
|
target->set_edge_weight(weight);
|
|
}
|
|
} else {
|
|
GotoInstr* jump = block->last_instruction()->AsGoto();
|
|
if (jump != NULL) {
|
|
intptr_t count =
|
|
GetEdgeCount(edge_counters, block->preorder_number());
|
|
if ((count >= 0) && (entry_count != 0)) {
|
|
double weight =
|
|
static_cast<double>(count) / static_cast<double>(entry_count);
|
|
jump->set_edge_weight(weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void BlockScheduler::AssignEdgeWeights() const {
|
|
if (!FLAG_emit_edge_counters) {
|
|
return;
|
|
}
|
|
|
|
const Array& ic_data_array = Array::Handle(flow_graph()->zone(),
|
|
flow_graph()->parsed_function().function().ic_data_array());
|
|
Array& edge_counters = Array::Handle();
|
|
edge_counters ^= ic_data_array.At(0);
|
|
|
|
intptr_t entry_count = GetEdgeCount(
|
|
edge_counters,
|
|
flow_graph()->graph_entry()->normal_entry()->preorder_number());
|
|
flow_graph()->graph_entry()->set_entry_count(entry_count);
|
|
|
|
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, NULL)), 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 != NULL; 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 != NULL; 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() const {
|
|
// 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);
|
|
}
|
|
|
|
// 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 != NULL; link = link->next) {
|
|
flow_graph()->CodegenBlockOrder(true)->Add(link->block);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|