| Index: src/hydrogen-load-elimination.cc
|
| diff --git a/src/hydrogen-load-elimination.cc b/src/hydrogen-load-elimination.cc
|
| deleted file mode 100644
|
| index a4536fd750aa456ef70d8bd083dfdf8c04fb5353..0000000000000000000000000000000000000000
|
| --- a/src/hydrogen-load-elimination.cc
|
| +++ /dev/null
|
| @@ -1,512 +0,0 @@
|
| -// Copyright 2013 the V8 project authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "src/hydrogen-alias-analysis.h"
|
| -#include "src/hydrogen-flow-engine.h"
|
| -#include "src/hydrogen-instructions.h"
|
| -#include "src/hydrogen-load-elimination.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;
|
| -
|
| -// An element in the field approximation list.
|
| -class HFieldApproximation : public ZoneObject {
|
| - public: // Just a data blob.
|
| - HValue* object_;
|
| - HValue* last_value_;
|
| - HFieldApproximation* next_;
|
| -
|
| - // Recursively copy the entire linked list of field approximations.
|
| - HFieldApproximation* Copy(Zone* zone) {
|
| - HFieldApproximation* copy = new(zone) HFieldApproximation();
|
| - copy->object_ = this->object_;
|
| - copy->last_value_ = this->last_value_;
|
| - copy->next_ = this->next_ == NULL ? NULL : 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 : 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 && l->CanBeReplacedWith(result)) {
|
| - // 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;
|
| - }
|
| - case HValue::kTransitionElementsKind: {
|
| - HTransitionElementsKind* t = HTransitionElementsKind::cast(instr);
|
| - HValue* object = t->object()->ActualValue();
|
| - KillFieldInternal(object, FieldOf(JSArray::kElementsOffset), NULL);
|
| - KillFieldInternal(object, FieldOf(JSObject::kMapOffset), NULL);
|
| - break;
|
| - }
|
| - default: {
|
| - if (instr->CheckChangesFlag(kInobjectFields)) {
|
| - TRACE((" kill-all i%d\n", instr->id()));
|
| - Kill();
|
| - break;
|
| - }
|
| - if (instr->CheckChangesFlag(kMaps)) {
|
| - TRACE((" kill-maps i%d\n", instr->id()));
|
| - KillOffset(JSObject::kMapOffset);
|
| - }
|
| - if (instr->CheckChangesFlag(kElementsKind)) {
|
| - TRACE((" kill-elements-kind i%d\n", instr->id()));
|
| - KillOffset(JSObject::kMapOffset);
|
| - KillOffset(JSObject::kElementsOffset);
|
| - }
|
| - if (instr->CheckChangesFlag(kElementsPointer)) {
|
| - TRACE((" kill-elements i%d\n", instr->id()));
|
| - KillOffset(JSObject::kElementsOffset);
|
| - }
|
| - if (instr->CheckChangesFlag(kOsrEntries)) {
|
| - 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: Merge given state with
|
| - // the other incoming state.
|
| - static HLoadEliminationTable* Merge(HLoadEliminationTable* succ_state,
|
| - HBasicBlock* succ_block,
|
| - HLoadEliminationTable* pred_state,
|
| - HBasicBlock* pred_block,
|
| - Zone* zone) {
|
| - DCHECK(pred_state != NULL);
|
| - if (succ_state == NULL) {
|
| - return pred_state->Copy(succ_block, pred_block, zone);
|
| - } else {
|
| - return succ_state->Merge(succ_block, pred_state, pred_block, zone);
|
| - }
|
| - }
|
| -
|
| - // Support for global analysis with HFlowEngine: Given state merged with all
|
| - // the other incoming states, prepare it for use.
|
| - static HLoadEliminationTable* Finish(HLoadEliminationTable* state,
|
| - HBasicBlock* block,
|
| - Zone* zone) {
|
| - DCHECK(state != NULL);
|
| - return state;
|
| - }
|
| -
|
| - private:
|
| - // Copy state to successor block.
|
| - HLoadEliminationTable* Copy(HBasicBlock* succ, HBasicBlock* from_block,
|
| - 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] == NULL ? NULL : fields_[i]->Copy(zone);
|
| - }
|
| - if (FLAG_trace_load_elimination) {
|
| - TRACE((" copy-to B%d\n", succ->block_id()));
|
| - copy->Print();
|
| - }
|
| - return copy;
|
| - }
|
| -
|
| - // Merge this state with the other incoming state.
|
| - HLoadEliminationTable* Merge(HBasicBlock* succ, HLoadEliminationTable* that,
|
| - HBasicBlock* that_block, 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_;
|
| - }
|
| - }
|
| - if (FLAG_trace_load_elimination) {
|
| - TRACE((" merge-to B%d\n", succ->block_id()));
|
| - Print();
|
| - }
|
| - 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}.
|
| - HValue* load(HLoadNamedField* instr) {
|
| - // There must be no loads from non observable in-object properties.
|
| - DCHECK(!instr->access().IsInobject() ||
|
| - instr->access().existing_inobject_property());
|
| -
|
| - int field = FieldOf(instr->access());
|
| - if (field < 0) return instr;
|
| -
|
| - HValue* object = instr->object()->ActualValue();
|
| - HFieldApproximation* approx = FindOrCreate(object, field);
|
| -
|
| - if (approx->last_value_ == NULL) {
|
| - // Load is not redundant. Fill out a new entry.
|
| - approx->last_value_ = instr;
|
| - return instr;
|
| - } else if (approx->last_value_->block()->EqualToOrDominates(
|
| - instr->block())) {
|
| - // Eliminate the load. Reuse previously stored value or load instruction.
|
| - return approx->last_value_;
|
| - } else {
|
| - return instr;
|
| - }
|
| - }
|
| -
|
| - // Process a store instruction, updating internal table state. If a previous
|
| - // store to the same object and field makes this store redundant (e.g. because
|
| - // the stored values are the same), return NULL indicating that this store
|
| - // instruction is redundant. Otherwise, return {instr}.
|
| - HValue* store(HStoreNamedField* instr) {
|
| - if (instr->access().IsInobject() &&
|
| - !instr->access().existing_inobject_property()) {
|
| - TRACE((" skipping non existing property initialization store\n"));
|
| - return instr;
|
| - }
|
| -
|
| - int field = FieldOf(instr->access());
|
| - if (field < 0) return KillIfMisaligned(instr);
|
| -
|
| - HValue* object = instr->object()->ActualValue();
|
| - HValue* value = instr->value();
|
| -
|
| - if (instr->has_transition()) {
|
| - // A transition introduces a new field and alters the map of the object.
|
| - // Since the field in the object is new, it cannot alias existing entries.
|
| - // TODO(titzer): introduce a constant for the new map and remember it.
|
| - KillFieldInternal(object, FieldOf(JSObject::kMapOffset), NULL);
|
| - } else {
|
| - // Kill non-equivalent may-alias entries.
|
| - KillFieldInternal(object, field, value);
|
| - }
|
| - HFieldApproximation* approx = FindOrCreate(object, field);
|
| -
|
| - if (Equal(approx->last_value_, value)) {
|
| - // The store is redundant because the field already has this value.
|
| - return NULL;
|
| - } else {
|
| - // The store is not redundant. Update the entry.
|
| - approx->last_value_ = value;
|
| - return instr;
|
| - }
|
| - }
|
| -
|
| - // Kill everything in this table.
|
| - void Kill() {
|
| - fields_.Rewind(0);
|
| - }
|
| -
|
| - // Kill all entries matching the given offset.
|
| - void KillOffset(int offset) {
|
| - int field = FieldOf(offset);
|
| - if (field >= 0 && field < fields_.length()) {
|
| - fields_[field] = NULL;
|
| - }
|
| - }
|
| -
|
| - // Kill all entries aliasing the given store.
|
| - void KillStore(HStoreNamedField* s) {
|
| - int field = FieldOf(s->access());
|
| - if (field >= 0) {
|
| - KillFieldInternal(s->object()->ActualValue(), field, s->value());
|
| - } else {
|
| - KillIfMisaligned(s);
|
| - }
|
| - }
|
| -
|
| - // Kill multiple entries in the case of a misaligned store.
|
| - HValue* KillIfMisaligned(HStoreNamedField* instr) {
|
| - HObjectAccess access = instr->access();
|
| - if (access.IsInobject()) {
|
| - int offset = access.offset();
|
| - if ((offset % kPointerSize) != 0) {
|
| - // Kill the field containing the first word of the access.
|
| - HValue* object = instr->object()->ActualValue();
|
| - int field = offset / kPointerSize;
|
| - KillFieldInternal(object, field, NULL);
|
| -
|
| - // Kill the next field in case of overlap.
|
| - int size = access.representation().size();
|
| - int next_field = (offset + size - 1) / kPointerSize;
|
| - if (next_field != field) KillFieldInternal(object, next_field, NULL);
|
| - }
|
| - }
|
| - return instr;
|
| - }
|
| -
|
| - // 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;
|
| - }
|
| -
|
| - // Find or create an entry for the given object and field pair.
|
| - HFieldApproximation* FindOrCreate(HValue* object, int field) {
|
| - EnsureFields(field + 1);
|
| -
|
| - // Search for a field approximation for this object.
|
| - HFieldApproximation* approx = fields_[field];
|
| - int count = 0;
|
| - while (approx != NULL) {
|
| - if (aliasing_->MustAlias(object, approx->object_)) return approx;
|
| - count++;
|
| - approx = approx->next_;
|
| - }
|
| -
|
| - if (count >= kMaxTrackedObjects) {
|
| - // Pull the last entry off the end and repurpose it for this object.
|
| - approx = ReuseLastApproximation(field);
|
| - } else {
|
| - // Allocate a new entry.
|
| - approx = new(zone_) HFieldApproximation();
|
| - }
|
| -
|
| - // Insert the entry at the head of the list.
|
| - approx->object_ = object;
|
| - approx->last_value_ = NULL;
|
| - approx->next_ = fields_[field];
|
| - fields_[field] = approx;
|
| -
|
| - return approx;
|
| - }
|
| -
|
| - // Kill all entries for a given field that _may_ alias the given object
|
| - // and do _not_ have the given value.
|
| - void KillFieldInternal(HValue* object, int field, HValue* value) {
|
| - if (field >= fields_.length()) return; // Nothing to do.
|
| -
|
| - HFieldApproximation* approx = fields_[field];
|
| - HFieldApproximation* prev = NULL;
|
| - while (approx != NULL) {
|
| - if (aliasing_->MayAlias(object, approx->object_)) {
|
| - if (!Equal(approx->last_value_, value)) {
|
| - // Kill an aliasing entry that doesn't agree on the value.
|
| - if (prev != NULL) {
|
| - prev->next_ = approx->next_;
|
| - } else {
|
| - fields_[field] = approx->next_;
|
| - }
|
| - approx = approx->next_;
|
| - continue;
|
| - }
|
| - }
|
| - prev = approx;
|
| - approx = approx->next_;
|
| - }
|
| - }
|
| -
|
| - bool Equal(HValue* a, HValue* b) {
|
| - if (a == b) return true;
|
| - if (a != NULL && b != NULL && a->CheckFlag(HValue::kUseGVN)) {
|
| - return a->Equals(b);
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Remove the last approximation for a field so that it can be reused.
|
| - // We reuse the last entry because it was the first inserted and is thus
|
| - // farthest away from the current instruction.
|
| - HFieldApproximation* ReuseLastApproximation(int field) {
|
| - HFieldApproximation* approx = fields_[field];
|
| - DCHECK(approx != NULL);
|
| -
|
| - HFieldApproximation* prev = NULL;
|
| - while (approx->next_ != NULL) {
|
| - prev = approx;
|
| - approx = approx->next_;
|
| - }
|
| - if (prev != NULL) prev->next_ = NULL;
|
| - return approx;
|
| - }
|
| -
|
| - // 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; -1 if not tracked.
|
| - int FieldOf(int offset) {
|
| - if (offset >= kMaxTrackedFields * kPointerSize) return -1;
|
| - // TODO(titzer): track misaligned loads in a separate list?
|
| - if ((offset % kPointerSize) != 0) return -1; // Ignore misaligned 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_value_ != NULL) PrintF(" v%d", a->last_value_->id());
|
| - PrintF("] ");
|
| - }
|
| - PrintF("\n");
|
| - }
|
| - }
|
| -
|
| - Zone* zone_;
|
| - ZoneList<HFieldApproximation*> fields_;
|
| - HAliasAnalyzer* aliasing_;
|
| -};
|
| -
|
| -
|
| -// Support for HFlowEngine: collect store effects within loops.
|
| -class HLoadEliminationEffects : public ZoneObject {
|
| - public:
|
| - explicit HLoadEliminationEffects(Zone* zone)
|
| - : zone_(zone), stores_(5, zone) { }
|
| -
|
| - inline bool Disabled() {
|
| - return false; // Effects are _not_ disabled.
|
| - }
|
| -
|
| - // Process a possibly side-effecting instruction.
|
| - void Process(HInstruction* instr, Zone* zone) {
|
| - if (instr->IsStoreNamedField()) {
|
| - stores_.Add(HStoreNamedField::cast(instr), zone_);
|
| - } else {
|
| - flags_.Add(instr->ChangesFlags());
|
| - }
|
| - }
|
| -
|
| - // Apply these effects to the given load elimination table.
|
| - void Apply(HLoadEliminationTable* table) {
|
| - // Loads must not be hoisted past the OSR entry, therefore we kill
|
| - // everything if we see an OSR entry.
|
| - if (flags_.Contains(kInobjectFields) || flags_.Contains(kOsrEntries)) {
|
| - table->Kill();
|
| - return;
|
| - }
|
| - if (flags_.Contains(kElementsKind) || flags_.Contains(kMaps)) {
|
| - table->KillOffset(JSObject::kMapOffset);
|
| - }
|
| - if (flags_.Contains(kElementsKind) || flags_.Contains(kElementsPointer)) {
|
| - table->KillOffset(JSObject::kElementsOffset);
|
| - }
|
| -
|
| - // Kill non-agreeing fields for each store contained in these effects.
|
| - for (int i = 0; i < stores_.length(); i++) {
|
| - table->KillStore(stores_[i]);
|
| - }
|
| - }
|
| -
|
| - // Union these effects with the other effects.
|
| - void Union(HLoadEliminationEffects* that, Zone* zone) {
|
| - flags_.Add(that->flags_);
|
| - for (int i = 0; i < that->stores_.length(); i++) {
|
| - stores_.Add(that->stores_[i], zone);
|
| - }
|
| - }
|
| -
|
| - private:
|
| - Zone* zone_;
|
| - GVNFlagSet flags_;
|
| - 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 internal
|
| -} // namespace v8
|
|
|