Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Unified Diff: runtime/vm/flow_graph_optimizer.cc

Issue 143263010: Implement simple dead store elimination. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/flow_graph_optimizer.cc
===================================================================
--- runtime/vm/flow_graph_optimizer.cc (revision 35904)
+++ runtime/vm/flow_graph_optimizer.cc (working copy)
@@ -27,6 +27,7 @@
DEFINE_FLAG(int, getter_setter_ratio, 13,
"Ratio of getter/setter usage used for double field unboxing heuristics");
DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination.");
+DEFINE_FLAG(bool, dead_store_elimination, true, "Eliminate dead stores");
DEFINE_FLAG(int, max_polymorphic_checks, 4,
"Maximum number of polymorphic check, otherwise it is megamorphic.");
DEFINE_FLAG(int, max_equality_polymorphic_checks, 32,
@@ -4098,6 +4099,7 @@
}
}
+
void FlowGraphOptimizer::VisitStaticCall(StaticCallInstr* call) {
MethodRecognizer::Kind recognized_kind =
MethodRecognizer::RecognizeKind(call->function());
@@ -5101,7 +5103,7 @@
// Load instructions handled by load elimination.
-static bool IsCandidateLoad(Instruction* instr) {
+static bool IsLoadEliminationCandidate(Instruction* instr) {
return instr->IsLoadField()
|| instr->IsLoadIndexed()
|| instr->IsLoadStaticField()
@@ -5112,7 +5114,7 @@
static bool IsLoopInvariantLoad(ZoneGrowableArray<BitVector*>* sets,
intptr_t loop_header_index,
Instruction* instr) {
- return IsCandidateLoad(instr) &&
+ return IsLoadEliminationCandidate(instr) &&
(sets != NULL) &&
instr->HasPlaceId() &&
((*sets)[loop_header_index] != NULL) &&
@@ -5178,14 +5180,6 @@
}
-static bool IsLoadEliminationCandidate(Definition* def) {
- return def->IsLoadField()
- || def->IsLoadIndexed()
- || def->IsLoadStaticField()
- || def->IsCurrentContext();
-}
-
-
// Alias represents a family of locations. It is used to capture aliasing
// between stores and loads. Store can alias another load or store if and only
// if they have the same alias.
@@ -5315,7 +5309,7 @@
// Construct a place from instruction if instruction accesses any place.
// Otherwise constructs kNone place.
- Place(Instruction* instr, bool* is_load)
+ Place(Instruction* instr, bool* is_load, bool* is_store)
: kind_(kNone),
representation_(kNoRepresentation),
instance_(NULL),
@@ -5350,6 +5344,7 @@
kind_ = kVMField;
offset_in_bytes_ = store->offset_in_bytes();
}
+ *is_store = true;
break;
}
@@ -5365,6 +5360,7 @@
representation_ = instr->AsStoreStaticField()->
RequiredInputRepresentation(StoreStaticFieldInstr::kValuePos);
field_ = &instr->AsStoreStaticField()->field();
+ *is_store = true;
break;
case Instruction::kLoadIndexed: {
@@ -5384,6 +5380,7 @@
RequiredInputRepresentation(StoreIndexedInstr::kValuePos);
instance_ = store_indexed->array()->definition()->OriginalDefinition();
index_ = store_indexed->index()->definition();
+ *is_store = true;
break;
}
@@ -5399,6 +5396,7 @@
ASSERT(instr->AsStoreContext()->RequiredInputRepresentation(
StoreContextInstr::kValuePos) == kTagged);
representation_ = kTagged;
+ *is_store = true;
break;
default:
@@ -6172,14 +6170,23 @@
return phi_moves;
}
+
+enum CSEMode {
+ kOptimizeLoads,
+ kOptimizeStores
+};
+
+
static AliasedSet* NumberPlaces(
FlowGraph* graph,
- DirectChainedHashMap<PointerKeyValueTrait<Place> >* map) {
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map,
+ CSEMode mode) {
// Loads representing different expression ids will be collected and
// used to build per offset kill sets.
ZoneGrowableArray<Place*>* places = new ZoneGrowableArray<Place*>(10);
bool has_loads = false;
+ bool has_stores = false;
for (BlockIterator it = graph->reverse_postorder_iterator();
!it.Done();
it.Advance()) {
@@ -6188,8 +6195,7 @@
!instr_it.Done();
instr_it.Advance()) {
Instruction* instr = instr_it.Current();
-
- Place place(instr, &has_loads);
+ Place place(instr, &has_loads, &has_stores);
if (place.kind() == Place::kNone) {
continue;
}
@@ -6212,9 +6218,12 @@
}
}
- if (!has_loads) {
+ if ((mode == kOptimizeLoads) && !has_loads) {
return NULL;
}
+ if ((mode == kOptimizeStores) && !has_stores) {
+ return NULL;
+ }
PhiPlaceMoves* phi_moves = ComputePhiMoves(map, places);
@@ -6268,7 +6277,7 @@
}
DirectChainedHashMap<PointerKeyValueTrait<Place> > map;
- AliasedSet* aliased_set = NumberPlaces(graph, &map);
+ AliasedSet* aliased_set = NumberPlaces(graph, &map, kOptimizeLoads);
if ((aliased_set != NULL) && !aliased_set->IsEmpty()) {
// If any loads were forwarded return true from Optimize to run load
// forwarding again. This will allow to forward chains of loads.
@@ -6346,9 +6355,9 @@
(array_store->class_id() == kTypedDataFloat64ArrayCid) ||
(array_store->class_id() == kTypedDataFloat32ArrayCid) ||
(array_store->class_id() == kTypedDataFloat32x4ArrayCid)) {
- bool is_load = false;
- Place store_place(instr, &is_load);
- ASSERT(!is_load);
+ bool is_load = false, is_store = false;
+ Place store_place(instr, &is_load, &is_store);
+ ASSERT(!is_load && is_store);
Place* place = map_->Lookup(&store_place);
if (place != NULL) {
// Store has a corresponding numbered place that might have a
@@ -7048,6 +7057,217 @@
};
+class StoreOptimizer : public LivenessAnalysis {
+ public:
+ StoreOptimizer(FlowGraph* graph,
+ AliasedSet* aliased_set,
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map)
+ : LivenessAnalysis(aliased_set->max_place_id(), graph->postorder()),
+ graph_(graph),
+ map_(map),
+ aliased_set_(aliased_set),
+ exposed_stores_(graph_->postorder().length()) {
+ const intptr_t num_blocks = graph_->postorder().length();
+ for (intptr_t i = 0; i < num_blocks; i++) {
+ exposed_stores_.Add(NULL);
+ }
+ }
+
+ static void OptimizeGraph(FlowGraph* graph) {
+ ASSERT(FLAG_load_cse);
+ if (FLAG_trace_load_optimization) {
+ FlowGraphPrinter::PrintGraph("Before StoreOptimizer", graph);
+ }
+
+ DirectChainedHashMap<PointerKeyValueTrait<Place> > map;
+ AliasedSet* aliased_set = NumberPlaces(graph, &map, kOptimizeStores);
+ if ((aliased_set != NULL) && !aliased_set->IsEmpty()) {
+ StoreOptimizer store_optimizer(graph, aliased_set, &map);
+ store_optimizer.Optimize();
+ }
+ }
+
+ private:
+ void Optimize() {
+ Analyze();
+ if (FLAG_trace_load_optimization) {
+ Dump();
+ }
+ EliminateDeadStores();
+ if (FLAG_trace_load_optimization) {
+ FlowGraphPrinter::PrintGraph("After StoreOptimizer", graph_);
+ }
+ }
+
+ bool CanEliminateStore(Instruction* instr) {
+ switch (instr->tag()) {
+ case Instruction::kStoreInstanceField:
+ if (instr->AsStoreInstanceField()->is_initialization()) {
+ // Can't eliminate stores that initialized unboxed fields.
+ return false;
+ }
+ case Instruction::kStoreContext:
+ case Instruction::kStoreIndexed:
+ case Instruction::kStoreStaticField:
+ return true;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ virtual void ComputeInitialSets() {
+ BitVector* all_places = new BitVector(aliased_set_->max_place_id());
+ all_places->SetAll();
+ for (BlockIterator block_it = graph_->postorder_iterator();
+ !block_it.Done();
+ block_it.Advance()) {
+ BlockEntryInstr* block = block_it.Current();
+ const intptr_t postorder_number = block->postorder_number();
+
+ BitVector* kill = kill_[postorder_number];
+ BitVector* live_in = live_in_[postorder_number];
+ BitVector* live_out = live_out_[postorder_number];
+
+ ZoneGrowableArray<Instruction*>* exposed_stores = NULL;
+
+ // Iterate backwards starting at the last instruction.
+ for (BackwardInstructionIterator instr_it(block);
+ !instr_it.Done();
+ instr_it.Advance()) {
+ Instruction* instr = instr_it.Current();
+
+ bool is_load = false;
+ bool is_store = false;
+ Place place(instr, &is_load, &is_store);
+ if (place.IsFinalField()) {
+ // Loads/stores of final fields do not participate.
+ continue;
+ }
+
+ // Handle stores.
+ if (is_store) {
+ if (kill->Contains(instr->place_id())) {
+ if (!live_in->Contains(instr->place_id()) &&
+ CanEliminateStore(instr)) {
+ if (FLAG_trace_optimization) {
+ OS::Print(
+ "Removing dead store to place %" Pd " in block B%" Pd "\n",
+ instr->place_id(), block->block_id());
+ }
+ instr_it.RemoveCurrentFromGraph();
+ }
+ } else if (!live_in->Contains(instr->place_id())) {
+ // Mark this store as down-ward exposed: They are the only
+ // candidates for the global store elimination.
+ if (exposed_stores == NULL) {
+ const intptr_t kMaxExposedStoresInitialSize = 5;
+ exposed_stores = new ZoneGrowableArray<Instruction*>(
+ Utils::Minimum(kMaxExposedStoresInitialSize,
+ aliased_set_->max_place_id()));
+ }
+ exposed_stores->Add(instr);
+ }
+ // Interfering stores kill only loads from the same place.
+ kill->Add(instr->place_id());
+ live_in->Remove(instr->place_id());
+ continue;
+ }
+
+ // Handle side effects, deoptimization and function return.
+ if (!instr->Effects().IsNone() ||
+ instr->CanDeoptimize() ||
+ instr->IsThrow() ||
+ instr->IsReThrow() ||
+ instr->IsReturn()) {
+ // Instructions that return from the function, instructions with side
+ // effects and instructions that can deoptimize are considered as
+ // loads from all places.
+ live_in->CopyFrom(all_places);
+ if (instr->IsThrow() || instr->IsReThrow() || instr->IsReturn()) {
+ // Initialize live-out for exit blocks since it won't be computed
+ // otherwise during the fixed point iteration.
+ live_out->CopyFrom(all_places);
+ }
+ continue;
+ }
+
+ // Handle loads.
+ Definition* defn = instr->AsDefinition();
+ if ((defn != NULL) && IsLoadEliminationCandidate(defn)) {
+ const Alias alias = aliased_set_->ComputeAlias(&place);
+ live_in->AddAll(aliased_set_->Get(alias));
+ continue;
+ }
+ }
+ exposed_stores_[postorder_number] = exposed_stores;
+ }
+ if (FLAG_trace_load_optimization) {
+ Dump();
+ OS::Print("---\n");
+ }
+ }
+
+ void EliminateDeadStores() {
+ // Iteration order does not matter here.
+ for (BlockIterator block_it = graph_->postorder_iterator();
+ !block_it.Done();
+ block_it.Advance()) {
+ BlockEntryInstr* block = block_it.Current();
+ const intptr_t postorder_number = block->postorder_number();
+
+ BitVector* live_out = live_out_[postorder_number];
+
+ ZoneGrowableArray<Instruction*>* exposed_stores =
+ exposed_stores_[postorder_number];
+ if (exposed_stores == NULL) continue; // No exposed stores.
+
+ // Iterate over candidate stores.
+ for (intptr_t i = 0; i < exposed_stores->length(); ++i) {
+ Instruction* instr = (*exposed_stores)[i];
+ bool is_load = false;
+ bool is_store = false;
+ Place place(instr, &is_load, &is_store);
+ ASSERT(!is_load && is_store);
+ if (place.IsFinalField()) {
+ // Final field do not participate in dead store elimination.
+ continue;
+ }
+ // Eliminate a downward exposed store if the corresponding place is not
+ // in live-out.
+ if (!live_out->Contains(instr->place_id()) &&
+ CanEliminateStore(instr)) {
+ if (FLAG_trace_optimization) {
+ OS::Print("Removing dead store to place %"Pd" in block B%"Pd"\n",
+ instr->place_id(), block->block_id());
+ }
+ instr->RemoveFromGraph(/* ignored */ false);
+ }
+ }
+ }
+ }
+
+ FlowGraph* graph_;
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map_;
+
+ // Mapping between field offsets in words and expression ids of loads from
+ // that offset.
+ AliasedSet* aliased_set_;
+
+ // Per block list of downward exposed stores.
+ GrowableArray<ZoneGrowableArray<Instruction*>*> exposed_stores_;
+
+ DISALLOW_COPY_AND_ASSIGN(StoreOptimizer);
+};
+
+
+void DeadStoreElimination::Optimize(FlowGraph* graph) {
+ if (FLAG_dead_store_elimination) {
+ StoreOptimizer::OptimizeGraph(graph);
+ }
+}
+
+
class CSEInstructionMap : public ValueObject {
public:
// Right now CSE and LICM track a single effect: possible externalization of
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698