Home | History | Annotate | Download | only in src
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "hydrogen-alias-analysis.h"
     29 #include "hydrogen-load-elimination.h"
     30 #include "hydrogen-instructions.h"
     31 #include "hydrogen-flow-engine.h"
     32 
     33 namespace v8 {
     34 namespace internal {
     35 
     36 #define GLOBAL true
     37 #define TRACE(x) if (FLAG_trace_load_elimination) PrintF x
     38 
     39 static const int kMaxTrackedFields = 16;
     40 static const int kMaxTrackedObjects = 5;
     41 
     42 // An element in the field approximation list.
     43 class HFieldApproximation : public ZoneObject {
     44  public:  // Just a data blob.
     45   HValue* object_;
     46   HLoadNamedField* last_load_;
     47   HValue* last_value_;
     48   HFieldApproximation* next_;
     49 
     50   // Recursively copy the entire linked list of field approximations.
     51   HFieldApproximation* Copy(Zone* zone) {
     52     if (this == NULL) return NULL;
     53     HFieldApproximation* copy = new(zone) HFieldApproximation();
     54     copy->object_ = this->object_;
     55     copy->last_load_ = this->last_load_;
     56     copy->last_value_ = this->last_value_;
     57     copy->next_ = this->next_->Copy(zone);
     58     return copy;
     59   }
     60 };
     61 
     62 
     63 // The main datastructure used during load/store elimination. Each in-object
     64 // field is tracked separately. For each field, store a list of known field
     65 // values for known objects.
     66 class HLoadEliminationTable : public ZoneObject {
     67  public:
     68   HLoadEliminationTable(Zone* zone, HAliasAnalyzer* aliasing)
     69     : zone_(zone), fields_(kMaxTrackedFields, zone), aliasing_(aliasing) { }
     70 
     71   // The main processing of instructions.
     72   HLoadEliminationTable* Process(HInstruction* instr, Zone* zone) {
     73     switch (instr->opcode()) {
     74       case HValue::kLoadNamedField: {
     75         HLoadNamedField* l = HLoadNamedField::cast(instr);
     76         TRACE((" process L%d field %d (o%d)\n",
     77                instr->id(),
     78                FieldOf(l->access()),
     79                l->object()->ActualValue()->id()));
     80         HValue* result = load(l);
     81         if (result != instr) {
     82           // The load can be replaced with a previous load or a value.
     83           TRACE(("  replace L%d -> v%d\n", instr->id(), result->id()));
     84           instr->DeleteAndReplaceWith(result);
     85         }
     86         break;
     87       }
     88       case HValue::kStoreNamedField: {
     89         HStoreNamedField* s = HStoreNamedField::cast(instr);
     90         TRACE((" process S%d field %d (o%d) = v%d\n",
     91                instr->id(),
     92                FieldOf(s->access()),
     93                s->object()->ActualValue()->id(),
     94                s->value()->id()));
     95         HValue* result = store(s);
     96         if (result == NULL) {
     97           // The store is redundant. Remove it.
     98           TRACE(("  remove S%d\n", instr->id()));
     99           instr->DeleteAndReplaceWith(NULL);
    100         }
    101         break;
    102       }
    103       default: {
    104         if (instr->CheckGVNFlag(kChangesInobjectFields)) {
    105           TRACE((" kill-all i%d\n", instr->id()));
    106           Kill();
    107           break;
    108         }
    109         if (instr->CheckGVNFlag(kChangesMaps)) {
    110           TRACE((" kill-maps i%d\n", instr->id()));
    111           KillOffset(JSObject::kMapOffset);
    112         }
    113         if (instr->CheckGVNFlag(kChangesElementsKind)) {
    114           TRACE((" kill-elements-kind i%d\n", instr->id()));
    115           KillOffset(JSObject::kMapOffset);
    116           KillOffset(JSObject::kElementsOffset);
    117         }
    118         if (instr->CheckGVNFlag(kChangesElementsPointer)) {
    119           TRACE((" kill-elements i%d\n", instr->id()));
    120           KillOffset(JSObject::kElementsOffset);
    121         }
    122         if (instr->CheckGVNFlag(kChangesOsrEntries)) {
    123           TRACE((" kill-osr i%d\n", instr->id()));
    124           Kill();
    125         }
    126       }
    127       // Improvements possible:
    128       // - learn from HCheckMaps for field 0
    129       // - remove unobservable stores (write-after-write)
    130       // - track cells
    131       // - track globals
    132       // - track roots
    133     }
    134     return this;
    135   }
    136 
    137   // Support for global analysis with HFlowEngine: Copy state to sucessor block.
    138   HLoadEliminationTable* Copy(HBasicBlock* succ, Zone* zone) {
    139     HLoadEliminationTable* copy =
    140         new(zone) HLoadEliminationTable(zone, aliasing_);
    141     copy->EnsureFields(fields_.length());
    142     for (int i = 0; i < fields_.length(); i++) {
    143       copy->fields_[i] = fields_[i]->Copy(zone);
    144     }
    145     if (FLAG_trace_load_elimination) {
    146       TRACE((" copy-to B%d\n", succ->block_id()));
    147       copy->Print();
    148     }
    149     return copy;
    150   }
    151 
    152   // Support for global analysis with HFlowEngine: Merge this state with
    153   // the other incoming state.
    154   HLoadEliminationTable* Merge(HBasicBlock* succ,
    155       HLoadEliminationTable* that, Zone* zone) {
    156     if (that->fields_.length() < fields_.length()) {
    157       // Drop fields not in the other table.
    158       fields_.Rewind(that->fields_.length());
    159     }
    160     for (int i = 0; i < fields_.length(); i++) {
    161       // Merge the field approximations for like fields.
    162       HFieldApproximation* approx = fields_[i];
    163       HFieldApproximation* prev = NULL;
    164       while (approx != NULL) {
    165         // TODO(titzer): Merging is O(N * M); sort?
    166         HFieldApproximation* other = that->Find(approx->object_, i);
    167         if (other == NULL || !Equal(approx->last_value_, other->last_value_)) {
    168           // Kill an entry that doesn't agree with the other value.
    169           if (prev != NULL) {
    170             prev->next_ = approx->next_;
    171           } else {
    172             fields_[i] = approx->next_;
    173           }
    174           approx = approx->next_;
    175           continue;
    176         }
    177         prev = approx;
    178         approx = approx->next_;
    179       }
    180     }
    181     return this;
    182   }
    183 
    184   friend class HLoadEliminationEffects;  // Calls Kill() and others.
    185   friend class HLoadEliminationPhase;
    186 
    187  private:
    188   // Process a load instruction, updating internal table state. If a previous
    189   // load or store for this object and field exists, return the new value with
    190   // which the load should be replaced. Otherwise, return {instr}.
    191   HValue* load(HLoadNamedField* instr) {
    192     int field = FieldOf(instr->access());
    193     if (field < 0) return instr;
    194 
    195     HValue* object = instr->object()->ActualValue();
    196     HFieldApproximation* approx = FindOrCreate(object, field);
    197 
    198     if (approx->last_value_ == NULL) {
    199       // Load is not redundant. Fill out a new entry.
    200       approx->last_load_ = instr;
    201       approx->last_value_ = instr;
    202       return instr;
    203     } else {
    204       // Eliminate the load. Reuse previously stored value or load instruction.
    205       return approx->last_value_;
    206     }
    207   }
    208 
    209   // Process a store instruction, updating internal table state. If a previous
    210   // store to the same object and field makes this store redundant (e.g. because
    211   // the stored values are the same), return NULL indicating that this store
    212   // instruction is redundant. Otherwise, return {instr}.
    213   HValue* store(HStoreNamedField* instr) {
    214     int field = FieldOf(instr->access());
    215     if (field < 0) return KillIfMisaligned(instr);
    216 
    217     HValue* object = instr->object()->ActualValue();
    218     HValue* value = instr->value();
    219 
    220     // Kill non-equivalent may-alias entries.
    221     KillFieldInternal(object, field, value);
    222     if (instr->has_transition()) {
    223       // A transition store alters the map of the object.
    224       // TODO(titzer): remember the new map (a constant) for the object.
    225       KillFieldInternal(object, FieldOf(JSObject::kMapOffset), NULL);
    226     }
    227     HFieldApproximation* approx = FindOrCreate(object, field);
    228 
    229     if (Equal(approx->last_value_, value)) {
    230       // The store is redundant because the field already has this value.
    231       return NULL;
    232     } else {
    233       // The store is not redundant. Update the entry.
    234       approx->last_load_ = NULL;
    235       approx->last_value_ = value;
    236       return instr;
    237     }
    238   }
    239 
    240   // Kill everything in this table.
    241   void Kill() {
    242     fields_.Rewind(0);
    243   }
    244 
    245   // Kill all entries matching the given offset.
    246   void KillOffset(int offset) {
    247     int field = FieldOf(offset);
    248     if (field >= 0 && field < fields_.length()) {
    249       fields_[field] = NULL;
    250     }
    251   }
    252 
    253   // Kill all entries aliasing the given store.
    254   void KillStore(HStoreNamedField* s) {
    255     int field = FieldOf(s->access());
    256     if (field >= 0) {
    257       KillFieldInternal(s->object()->ActualValue(), field, s->value());
    258     } else {
    259       KillIfMisaligned(s);
    260     }
    261   }
    262 
    263   // Kill multiple entries in the case of a misaligned store.
    264   HValue* KillIfMisaligned(HStoreNamedField* instr) {
    265     HObjectAccess access = instr->access();
    266     if (access.IsInobject()) {
    267       int offset = access.offset();
    268       if ((offset % kPointerSize) != 0) {
    269         // Kill the field containing the first word of the access.
    270         HValue* object = instr->object()->ActualValue();
    271         int field = offset / kPointerSize;
    272         KillFieldInternal(object, field, NULL);
    273 
    274         // Kill the next field in case of overlap.
    275         int size = access.representation().size();
    276         int next_field = (offset + size - 1) / kPointerSize;
    277         if (next_field != field) KillFieldInternal(object, next_field, NULL);
    278       }
    279     }
    280     return instr;
    281   }
    282 
    283   // Find an entry for the given object and field pair.
    284   HFieldApproximation* Find(HValue* object, int field) {
    285     // Search for a field approximation for this object.
    286     HFieldApproximation* approx = fields_[field];
    287     while (approx != NULL) {
    288       if (aliasing_->MustAlias(object, approx->object_)) return approx;
    289       approx = approx->next_;
    290     }
    291     return NULL;
    292   }
    293 
    294   // Find or create an entry for the given object and field pair.
    295   HFieldApproximation* FindOrCreate(HValue* object, int field) {
    296     EnsureFields(field + 1);
    297 
    298     // Search for a field approximation for this object.
    299     HFieldApproximation* approx = fields_[field];
    300     int count = 0;
    301     while (approx != NULL) {
    302       if (aliasing_->MustAlias(object, approx->object_)) return approx;
    303       count++;
    304       approx = approx->next_;
    305     }
    306 
    307     if (count >= kMaxTrackedObjects) {
    308       // Pull the last entry off the end and repurpose it for this object.
    309       approx = ReuseLastApproximation(field);
    310     } else {
    311       // Allocate a new entry.
    312       approx = new(zone_) HFieldApproximation();
    313     }
    314 
    315     // Insert the entry at the head of the list.
    316     approx->object_ = object;
    317     approx->last_load_ = NULL;
    318     approx->last_value_ = NULL;
    319     approx->next_ = fields_[field];
    320     fields_[field] = approx;
    321 
    322     return approx;
    323   }
    324 
    325   // Kill all entries for a given field that _may_ alias the given object
    326   // and do _not_ have the given value.
    327   void KillFieldInternal(HValue* object, int field, HValue* value) {
    328     if (field >= fields_.length()) return;  // Nothing to do.
    329 
    330     HFieldApproximation* approx = fields_[field];
    331     HFieldApproximation* prev = NULL;
    332     while (approx != NULL) {
    333       if (aliasing_->MayAlias(object, approx->object_)) {
    334         if (!Equal(approx->last_value_, value)) {
    335           // Kill an aliasing entry that doesn't agree on the value.
    336           if (prev != NULL) {
    337             prev->next_ = approx->next_;
    338           } else {
    339             fields_[field] = approx->next_;
    340           }
    341           approx = approx->next_;
    342           continue;
    343         }
    344       }
    345       prev = approx;
    346       approx = approx->next_;
    347     }
    348   }
    349 
    350   bool Equal(HValue* a, HValue* b) {
    351     if (a == b) return true;
    352     if (a != NULL && b != NULL && a->CheckFlag(HValue::kUseGVN)) {
    353       return a->Equals(b);
    354     }
    355     return false;
    356   }
    357 
    358   // Remove the last approximation for a field so that it can be reused.
    359   // We reuse the last entry because it was the first inserted and is thus
    360   // farthest away from the current instruction.
    361   HFieldApproximation* ReuseLastApproximation(int field) {
    362     HFieldApproximation* approx = fields_[field];
    363     ASSERT(approx != NULL);
    364 
    365     HFieldApproximation* prev = NULL;
    366     while (approx->next_ != NULL) {
    367       prev = approx;
    368       approx = approx->next_;
    369     }
    370     if (prev != NULL) prev->next_ = NULL;
    371     return approx;
    372   }
    373 
    374   // Compute the field index for the given object access; -1 if not tracked.
    375   int FieldOf(HObjectAccess access) {
    376     return access.IsInobject() ? FieldOf(access.offset()) : -1;
    377   }
    378 
    379   // Compute the field index for the given in-object offset; -1 if not tracked.
    380   int FieldOf(int offset) {
    381     if (offset >= kMaxTrackedFields * kPointerSize) return -1;
    382     // TODO(titzer): track misaligned loads in a separate list?
    383     if ((offset % kPointerSize) != 0) return -1;  // Ignore misaligned accesses.
    384     return offset / kPointerSize;
    385   }
    386 
    387   // Ensure internal storage for the given number of fields.
    388   void EnsureFields(int num_fields) {
    389     if (fields_.length() < num_fields) {
    390       fields_.AddBlock(NULL, num_fields - fields_.length(), zone_);
    391     }
    392   }
    393 
    394   // Print this table to stdout.
    395   void Print() {
    396     for (int i = 0; i < fields_.length(); i++) {
    397       PrintF("  field %d: ", i);
    398       for (HFieldApproximation* a = fields_[i]; a != NULL; a = a->next_) {
    399         PrintF("[o%d =", a->object_->id());
    400         if (a->last_load_ != NULL) PrintF(" L%d", a->last_load_->id());
    401         if (a->last_value_ != NULL) PrintF(" v%d", a->last_value_->id());
    402         PrintF("] ");
    403       }
    404       PrintF("\n");
    405     }
    406   }
    407 
    408   Zone* zone_;
    409   ZoneList<HFieldApproximation*> fields_;
    410   HAliasAnalyzer* aliasing_;
    411 };
    412 
    413 
    414 // Support for HFlowEngine: collect store effects within loops.
    415 class HLoadEliminationEffects : public ZoneObject {
    416  public:
    417   explicit HLoadEliminationEffects(Zone* zone)
    418     : zone_(zone),
    419       maps_stored_(false),
    420       fields_stored_(false),
    421       elements_stored_(false),
    422       stores_(5, zone) { }
    423 
    424   inline bool Disabled() {
    425     return false;  // Effects are _not_ disabled.
    426   }
    427 
    428   // Process a possibly side-effecting instruction.
    429   void Process(HInstruction* instr, Zone* zone) {
    430     switch (instr->opcode()) {
    431       case HValue::kStoreNamedField: {
    432         stores_.Add(HStoreNamedField::cast(instr), zone_);
    433         break;
    434       }
    435       case HValue::kOsrEntry: {
    436         // Kill everything. Loads must not be hoisted past the OSR entry.
    437         maps_stored_ = true;
    438         fields_stored_ = true;
    439         elements_stored_ = true;
    440       }
    441       default: {
    442         fields_stored_ |= instr->CheckGVNFlag(kChangesInobjectFields);
    443         maps_stored_ |= instr->CheckGVNFlag(kChangesMaps);
    444         maps_stored_ |= instr->CheckGVNFlag(kChangesElementsKind);
    445         elements_stored_ |= instr->CheckGVNFlag(kChangesElementsKind);
    446         elements_stored_ |= instr->CheckGVNFlag(kChangesElementsPointer);
    447       }
    448     }
    449   }
    450 
    451   // Apply these effects to the given load elimination table.
    452   void Apply(HLoadEliminationTable* table) {
    453     if (fields_stored_) {
    454       table->Kill();
    455       return;
    456     }
    457     if (maps_stored_) {
    458       table->KillOffset(JSObject::kMapOffset);
    459     }
    460     if (elements_stored_) {
    461       table->KillOffset(JSObject::kElementsOffset);
    462     }
    463 
    464     // Kill non-agreeing fields for each store contained in these effects.
    465     for (int i = 0; i < stores_.length(); i++) {
    466       table->KillStore(stores_[i]);
    467     }
    468   }
    469 
    470   // Union these effects with the other effects.
    471   void Union(HLoadEliminationEffects* that, Zone* zone) {
    472     maps_stored_ |= that->maps_stored_;
    473     fields_stored_ |= that->fields_stored_;
    474     elements_stored_ |= that->elements_stored_;
    475     for (int i = 0; i < that->stores_.length(); i++) {
    476       stores_.Add(that->stores_[i], zone);
    477     }
    478   }
    479 
    480  private:
    481   Zone* zone_;
    482   bool maps_stored_ : 1;
    483   bool fields_stored_ : 1;
    484   bool elements_stored_ : 1;
    485   ZoneList<HStoreNamedField*> stores_;
    486 };
    487 
    488 
    489 // The main routine of the analysis phase. Use the HFlowEngine for either a
    490 // local or a global analysis.
    491 void HLoadEliminationPhase::Run() {
    492   HFlowEngine<HLoadEliminationTable, HLoadEliminationEffects>
    493     engine(graph(), zone());
    494   HAliasAnalyzer aliasing;
    495   HLoadEliminationTable* table =
    496       new(zone()) HLoadEliminationTable(zone(), &aliasing);
    497 
    498   if (GLOBAL) {
    499     // Perform a global analysis.
    500     engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), table);
    501   } else {
    502     // Perform only local analysis.
    503     for (int i = 0; i < graph()->blocks()->length(); i++) {
    504       table->Kill();
    505       engine.AnalyzeOneBlock(graph()->blocks()->at(i), table);
    506     }
    507   }
    508 }
    509 
    510 } }  // namespace v8::internal
    511