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

Unified Diff: src/hydrogen-load-elimination.cc

Issue 27148004: Implement global load elimination based on flow engine. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Add a nested loop to the global load elimination test case. Created 7 years, 2 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 | « no previous file | test/mjsunit/compiler/load-elimination-global.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/hydrogen-load-elimination.cc
diff --git a/src/hydrogen-load-elimination.cc b/src/hydrogen-load-elimination.cc
index 6d01ae573be81d1dd2e86444cfa88af0a9091762..f33ebe2fabcb61aed1429792618e745e58de73ce 100644
--- a/src/hydrogen-load-elimination.cc
+++ b/src/hydrogen-load-elimination.cc
@@ -28,10 +28,14 @@
#include "hydrogen-alias-analysis.h"
#include "hydrogen-load-elimination.h"
#include "hydrogen-instructions.h"
+#include "hydrogen-flow-engine.h"
namespace v8 {
namespace internal {
+#define GLOBAL true
+#define TRACE(x) if (FLAG_trace_load_elimination) PrintF x
+
static const int kMaxTrackedFields = 16;
static const int kMaxTrackedObjects = 5;
@@ -42,17 +46,145 @@ class HFieldApproximation : public ZoneObject {
HLoadNamedField* last_load_;
HValue* last_value_;
HFieldApproximation* next_;
+
+ // Recursively copy the entire linked list of field approximations.
+ HFieldApproximation* Copy(Zone* zone) {
+ if (this == NULL) return NULL;
+ HFieldApproximation* copy = new(zone) HFieldApproximation();
+ copy->object_ = this->object_;
+ copy->last_load_ = this->last_load_;
+ copy->last_value_ = this->last_value_;
+ copy->next_ = this->next_->Copy(zone);
+ return copy;
+ }
};
// The main datastructure used during load/store elimination. Each in-object
// field is tracked separately. For each field, store a list of known field
// values for known objects.
-class HLoadEliminationTable BASE_EMBEDDED {
+class HLoadEliminationTable : public ZoneObject {
public:
HLoadEliminationTable(Zone* zone, HAliasAnalyzer* aliasing)
: zone_(zone), fields_(kMaxTrackedFields, zone), aliasing_(aliasing) { }
+ // The main processing of instructions.
+ HLoadEliminationTable* Process(HInstruction* instr, Zone* zone) {
+ switch (instr->opcode()) {
+ case HValue::kLoadNamedField: {
+ HLoadNamedField* l = HLoadNamedField::cast(instr);
+ TRACE((" process L%d field %d (o%d)\n",
+ instr->id(),
+ FieldOf(l->access()),
+ l->object()->ActualValue()->id()));
+ HValue* result = load(l);
+ if (result != instr) {
+ // The load can be replaced with a previous load or a value.
+ TRACE((" replace L%d -> v%d\n", instr->id(), result->id()));
+ instr->DeleteAndReplaceWith(result);
+ }
+ break;
+ }
+ case HValue::kStoreNamedField: {
+ HStoreNamedField* s = HStoreNamedField::cast(instr);
+ TRACE((" process S%d field %d (o%d) = v%d\n",
+ instr->id(),
+ FieldOf(s->access()),
+ s->object()->ActualValue()->id(),
+ s->value()->id()));
+ HValue* result = store(s);
+ if (result == NULL) {
+ // The store is redundant. Remove it.
+ TRACE((" remove S%d\n", instr->id()));
+ instr->DeleteAndReplaceWith(NULL);
+ }
+ break;
+ }
+ default: {
+ if (instr->CheckGVNFlag(kChangesInobjectFields)) {
+ TRACE((" kill-all i%d\n", instr->id()));
+ Kill();
+ break;
+ }
+ if (instr->CheckGVNFlag(kChangesMaps)) {
+ TRACE((" kill-maps i%d\n", instr->id()));
+ KillOffset(JSObject::kMapOffset);
+ }
+ if (instr->CheckGVNFlag(kChangesElementsKind)) {
+ TRACE((" kill-elements-kind i%d\n", instr->id()));
+ KillOffset(JSObject::kMapOffset);
+ KillOffset(JSObject::kElementsOffset);
+ }
+ if (instr->CheckGVNFlag(kChangesElementsPointer)) {
+ TRACE((" kill-elements i%d\n", instr->id()));
+ KillOffset(JSObject::kElementsOffset);
+ }
+ if (instr->CheckGVNFlag(kChangesOsrEntries)) {
+ TRACE((" kill-osr i%d\n", instr->id()));
+ Kill();
+ }
+ }
+ // Improvements possible:
+ // - learn from HCheckMaps for field 0
+ // - remove unobservable stores (write-after-write)
+ // - track cells
+ // - track globals
+ // - track roots
+ }
+ return this;
+ }
+
+ // Support for global analysis with HFlowEngine: Copy state to sucessor block.
+ HLoadEliminationTable* Copy(HBasicBlock* succ, Zone* zone) {
+ HLoadEliminationTable* copy =
+ new(zone) HLoadEliminationTable(zone, aliasing_);
+ copy->EnsureFields(fields_.length());
+ for (int i = 0; i < fields_.length(); i++) {
+ copy->fields_[i] = fields_[i]->Copy(zone);
+ }
+ if (FLAG_trace_load_elimination) {
+ TRACE((" copy-to B%d\n", succ->block_id()));
+ copy->Print();
+ }
+ return copy;
+ }
+
+ // Support for global analysis with HFlowEngine: Merge this state with
+ // the other incoming state.
+ HLoadEliminationTable* Merge(HBasicBlock* succ,
+ HLoadEliminationTable* that, Zone* zone) {
+ if (that->fields_.length() < fields_.length()) {
+ // Drop fields not in the other table.
+ fields_.Rewind(that->fields_.length());
+ }
+ for (int i = 0; i < fields_.length(); i++) {
+ // Merge the field approximations for like fields.
+ HFieldApproximation* approx = fields_[i];
+ HFieldApproximation* prev = NULL;
+ while (approx != NULL) {
+ // TODO(titzer): Merging is O(N * M); sort?
+ HFieldApproximation* other = that->Find(approx->object_, i);
+ if (other == NULL || !Equal(approx->last_value_, other->last_value_)) {
+ // Kill an entry that doesn't agree with the other value.
+ if (prev != NULL) {
+ prev->next_ = approx->next_;
+ } else {
+ fields_[i] = approx->next_;
+ }
+ approx = approx->next_;
+ continue;
+ }
+ prev = approx;
+ approx = approx->next_;
+ }
+ }
+ return this;
+ }
+
+ friend class HLoadEliminationEffects; // Calls Kill() and others.
+ friend class HLoadEliminationPhase;
+
+ private:
// Process a load instruction, updating internal table state. If a previous
// load or store for this object and field exists, return the new value with
// which the load should be replaced. Otherwise, return {instr}.
@@ -118,28 +250,17 @@ class HLoadEliminationTable BASE_EMBEDDED {
}
}
- // Compute the field index for the given object access; -1 if not tracked.
- int FieldOf(HObjectAccess access) {
- // Only track kMaxTrackedFields in-object fields.
- if (!access.IsInobject()) return -1;
- return FieldOf(access.offset());
- }
-
- // Print this table to stdout.
- void Print() {
- for (int i = 0; i < fields_.length(); i++) {
- PrintF(" field %d: ", i);
- for (HFieldApproximation* a = fields_[i]; a != NULL; a = a->next_) {
- PrintF("[o%d =", a->object_->id());
- if (a->last_load_ != NULL) PrintF(" L%d", a->last_load_->id());
- if (a->last_value_ != NULL) PrintF(" v%d", a->last_value_->id());
- PrintF("] ");
- }
- PrintF("\n");
+ // Find an entry for the given object and field pair.
+ HFieldApproximation* Find(HValue* object, int field) {
+ // Search for a field approximation for this object.
+ HFieldApproximation* approx = fields_[field];
+ while (approx != NULL) {
+ if (aliasing_->MustAlias(object, approx->object_)) return approx;
+ approx = approx->next_;
}
+ return NULL;
}
- private:
// Find or create an entry for the given object and field pair.
HFieldApproximation* FindOrCreate(HValue* object, int field) {
EnsureFields(field + 1);
@@ -218,110 +339,143 @@ class HLoadEliminationTable BASE_EMBEDDED {
return approx;
}
- // Ensure internal storage for the given number of fields.
- void EnsureFields(int num_fields) {
- while (fields_.length() < num_fields) fields_.Add(NULL, zone_);
+ // Compute the field index for the given object access; -1 if not tracked.
+ int FieldOf(HObjectAccess access) {
+ return access.IsInobject() ? FieldOf(access.offset()) : -1;
}
- // Compute the field index for the given in-object offset.
+ // Compute the field index for the given in-object offset; -1 if not tracked.
int FieldOf(int offset) {
if (offset >= kMaxTrackedFields * kPointerSize) return -1;
ASSERT((offset % kPointerSize) == 0); // Assume aligned accesses.
return offset / kPointerSize;
}
+ // Ensure internal storage for the given number of fields.
+ void EnsureFields(int num_fields) {
+ if (fields_.length() < num_fields) {
+ fields_.AddBlock(NULL, num_fields - fields_.length(), zone_);
+ }
+ }
+
+ // Print this table to stdout.
+ void Print() {
+ for (int i = 0; i < fields_.length(); i++) {
+ PrintF(" field %d: ", i);
+ for (HFieldApproximation* a = fields_[i]; a != NULL; a = a->next_) {
+ PrintF("[o%d =", a->object_->id());
+ if (a->last_load_ != NULL) PrintF(" L%d", a->last_load_->id());
+ if (a->last_value_ != NULL) PrintF(" v%d", a->last_value_->id());
+ PrintF("] ");
+ }
+ PrintF("\n");
+ }
+ }
+
Zone* zone_;
ZoneList<HFieldApproximation*> fields_;
HAliasAnalyzer* aliasing_;
};
-void HLoadEliminationPhase::Run() {
- for (int i = 0; i < graph()->blocks()->length(); i++) {
- HBasicBlock* block = graph()->blocks()->at(i);
- EliminateLoads(block);
+// Support for HFlowEngine: collect store effects within loops.
+class HLoadEliminationEffects : public ZoneObject {
+ public:
+ explicit HLoadEliminationEffects(Zone* zone)
+ : zone_(zone),
+ maps_stored_(false),
+ fields_stored_(false),
+ elements_stored_(false),
+ stores_(5, zone) { }
+
+ inline bool Disabled() {
+ return false; // Effects are _not_ disabled.
}
-}
-
-
-// For code de-uglification.
-#define TRACE(x) if (FLAG_trace_load_elimination) PrintF x
-
-
-// Eliminate loads and stores local to a block.
-void HLoadEliminationPhase::EliminateLoads(HBasicBlock* block) {
- HAliasAnalyzer aliasing;
- HLoadEliminationTable table(zone(), &aliasing);
-
- TRACE(("-- load-elim B%d -------------------------------------------------\n",
- block->block_id()));
-
- for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
- bool changed = false;
- HInstruction* instr = it.Current();
+ // Process a possibly side-effecting instruction.
+ void Process(HInstruction* instr, Zone* zone) {
switch (instr->opcode()) {
- case HValue::kLoadNamedField: {
- HLoadNamedField* load = HLoadNamedField::cast(instr);
- TRACE((" process L%d field %d (o%d)\n",
- instr->id(),
- table.FieldOf(load->access()),
- load->object()->ActualValue()->id()));
- HValue* result = table.load(load);
- if (result != instr) {
- // The load can be replaced with a previous load or a value.
- TRACE((" replace L%d -> v%d\n", instr->id(), result->id()));
- instr->DeleteAndReplaceWith(result);
- }
- changed = true;
- break;
- }
case HValue::kStoreNamedField: {
- HStoreNamedField* store = HStoreNamedField::cast(instr);
- TRACE((" process S%d field %d (o%d) = v%d\n",
- instr->id(),
- table.FieldOf(store->access()),
- store->object()->ActualValue()->id(),
- store->value()->id()));
- HValue* result = table.store(store);
- if (result == NULL) {
- // The store is redundant. Remove it.
- TRACE((" remove S%d\n", instr->id()));
- instr->DeleteAndReplaceWith(NULL);
- }
- changed = true;
+ stores_.Add(HStoreNamedField::cast(instr), zone_);
break;
}
+ case HValue::kOsrEntry: {
+ // Kill everything. Loads must not be hoisted past the OSR entry.
+ maps_stored_ = true;
+ fields_stored_ = true;
+ elements_stored_ = true;
+ }
default: {
- if (instr->CheckGVNFlag(kChangesInobjectFields)) {
- TRACE((" kill-all i%d\n", instr->id()));
- table.Kill();
- continue;
- }
- if (instr->CheckGVNFlag(kChangesMaps)) {
- TRACE((" kill-maps i%d\n", instr->id()));
- table.KillOffset(JSObject::kMapOffset);
- }
- if (instr->CheckGVNFlag(kChangesElementsKind)) {
- TRACE((" kill-elements-kind i%d\n", instr->id()));
- table.KillOffset(JSObject::kMapOffset);
- table.KillOffset(JSObject::kElementsOffset);
- }
- if (instr->CheckGVNFlag(kChangesElementsPointer)) {
- TRACE((" kill-elements i%d\n", instr->id()));
- table.KillOffset(JSObject::kElementsOffset);
- }
+ fields_stored_ |= instr->CheckGVNFlag(kChangesInobjectFields);
+ maps_stored_ |= instr->CheckGVNFlag(kChangesMaps);
+ maps_stored_ |= instr->CheckGVNFlag(kChangesElementsKind);
+ elements_stored_ |= instr->CheckGVNFlag(kChangesElementsKind);
+ elements_stored_ |= instr->CheckGVNFlag(kChangesElementsPointer);
}
- // Improvements possible:
- // - learn from HCheckMaps for field 0
- // - remove unobservable stores (write-after-write)
}
+ }
- if (changed && FLAG_trace_load_elimination) {
- table.Print();
+ // Apply these effects to the given load elimination table.
+ void Apply(HLoadEliminationTable* table) {
+ if (fields_stored_) {
+ table->Kill();
+ return;
+ }
+ if (maps_stored_) {
+ table->KillOffset(JSObject::kMapOffset);
+ }
+ if (elements_stored_) {
+ table->KillOffset(JSObject::kElementsOffset);
+ }
+
+ // Kill non-agreeing fields for each store contained in these effects.
+ for (int i = 0; i < stores_.length(); i++) {
+ HStoreNamedField* s = stores_[i];
+ int field = table->FieldOf(s->access());
+ if (field >= 0) {
+ table->KillFieldInternal(s->object()->ActualValue(), field, s->value());
+ }
+ }
+ }
+
+ // Union these effects with the other effects.
+ void Union(HLoadEliminationEffects* that, Zone* zone) {
+ maps_stored_ |= that->maps_stored_;
+ fields_stored_ |= that->fields_stored_;
+ elements_stored_ |= that->elements_stored_;
+ for (int i = 0; i < that->stores_.length(); i++) {
+ stores_.Add(that->stores_[i], zone);
}
}
-}
+ private:
+ Zone* zone_;
+ bool maps_stored_ : 1;
+ bool fields_stored_ : 1;
+ bool elements_stored_ : 1;
+ ZoneList<HStoreNamedField*> stores_;
+};
+
+
+// The main routine of the analysis phase. Use the HFlowEngine for either a
+// local or a global analysis.
+void HLoadEliminationPhase::Run() {
+ HFlowEngine<HLoadEliminationTable, HLoadEliminationEffects>
+ engine(graph(), zone());
+ HAliasAnalyzer aliasing;
+ HLoadEliminationTable* table =
+ new(zone()) HLoadEliminationTable(zone(), &aliasing);
+
+ if (GLOBAL) {
+ // Perform a global analysis.
+ engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), table);
+ } else {
+ // Perform only local analysis.
+ for (int i = 0; i < graph()->blocks()->length(); i++) {
+ table->Kill();
+ engine.AnalyzeOneBlock(graph()->blocks()->at(i), table);
+ }
+ }
+}
} } // namespace v8::internal
« no previous file with comments | « no previous file | test/mjsunit/compiler/load-elimination-global.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698