| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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-check-elimination.h" | 
|  | 29 #include "hydrogen-alias-analysis.h" | 
|  | 30 | 
|  | 31 namespace v8 { | 
|  | 32 namespace internal { | 
|  | 33 | 
|  | 34 static const int kMaxTrackedObjects = 10; | 
|  | 35 typedef UniqueSet<Map>* MapSet; | 
|  | 36 | 
|  | 37 // The main datastructure used during check elimination, which stores a | 
|  | 38 // set of known maps for each object. | 
|  | 39 class HCheckTable { | 
|  | 40  public: | 
|  | 41   explicit HCheckTable(Zone* zone) : zone_(zone) { | 
|  | 42     Kill(); | 
|  | 43     redundant_ = 0; | 
|  | 44     narrowed_ = 0; | 
|  | 45     empty_ = 0; | 
|  | 46     removed_ = 0; | 
|  | 47     compares_true_ = 0; | 
|  | 48     compares_false_ = 0; | 
|  | 49     transitions_ = 0; | 
|  | 50     loads_ = 0; | 
|  | 51   } | 
|  | 52 | 
|  | 53   void ReduceCheckMaps(HCheckMaps* instr) { | 
|  | 54     HValue* object = instr->value()->ActualValue(); | 
|  | 55     int index = Find(object); | 
|  | 56     if (index >= 0) { | 
|  | 57       // entry found; | 
|  | 58       MapSet a = known_maps_[index]; | 
|  | 59       MapSet i = instr->map_set().Copy(zone_); | 
|  | 60       if (a->IsSubset(i)) { | 
|  | 61         // The first check is more strict; the second is redundant. | 
|  | 62         if (checks_[index] != NULL) { | 
|  | 63           instr->DeleteAndReplaceWith(checks_[index]); | 
|  | 64           redundant_++; | 
|  | 65         } else { | 
|  | 66           instr->DeleteAndReplaceWith(instr->value()); | 
|  | 67           removed_++; | 
|  | 68         } | 
|  | 69         return; | 
|  | 70       } | 
|  | 71       i = i->Intersect(a, zone_); | 
|  | 72       if (i->size() == 0) { | 
|  | 73         // Intersection is empty; probably megamorphic, which is likely to | 
|  | 74         // deopt anyway, so just leave things as they are. | 
|  | 75         empty_++; | 
|  | 76       } else { | 
|  | 77         // TODO(titzer): replace the first check with a more strict check. | 
|  | 78         narrowed_++; | 
|  | 79       } | 
|  | 80     } else { | 
|  | 81       // No entry; insert a new one. | 
|  | 82       Insert(object, instr, instr->map_set().Copy(zone_)); | 
|  | 83     } | 
|  | 84   } | 
|  | 85 | 
|  | 86   void ReduceCheckValue(HCheckValue* instr) { | 
|  | 87     // Canonicalize HCheckValues; they might have their values load-eliminated. | 
|  | 88     HValue* value = instr->Canonicalize(); | 
|  | 89     if (value == NULL) { | 
|  | 90       instr->DeleteAndReplaceWith(instr->value()); | 
|  | 91       removed_++; | 
|  | 92     } else if (value != instr) { | 
|  | 93       instr->DeleteAndReplaceWith(value); | 
|  | 94       redundant_++; | 
|  | 95     } | 
|  | 96   } | 
|  | 97 | 
|  | 98   void ReduceLoadNamedField(HLoadNamedField* instr) { | 
|  | 99     // Reduce a load of the map field when it is known to be a constant. | 
|  | 100     if (!IsMapAccess(instr->access())) return; | 
|  | 101 | 
|  | 102     HValue* object = instr->object()->ActualValue(); | 
|  | 103     MapSet maps = FindMaps(object); | 
|  | 104     if (maps == NULL || maps->size() != 1) return;  // Not a constant. | 
|  | 105 | 
|  | 106     Unique<Map> map = maps->at(0); | 
|  | 107     HConstant* constant = HConstant::CreateAndInsertBefore( | 
|  | 108         instr->block()->graph()->zone(), map, true, instr); | 
|  | 109     instr->DeleteAndReplaceWith(constant); | 
|  | 110     loads_++; | 
|  | 111   } | 
|  | 112 | 
|  | 113   void ReduceCheckMapValue(HCheckMapValue* instr) { | 
|  | 114     if (!instr->map()->IsConstant()) return;  // Nothing to learn. | 
|  | 115 | 
|  | 116     HValue* object = instr->value()->ActualValue(); | 
|  | 117     // Match a HCheckMapValue(object, HConstant(map)) | 
|  | 118     Unique<Map> map = MapConstant(instr->map()); | 
|  | 119     MapSet maps = FindMaps(object); | 
|  | 120     if (maps != NULL) { | 
|  | 121       if (maps->Contains(map)) { | 
|  | 122         if (maps->size() == 1) { | 
|  | 123           // Object is known to have exactly this map. | 
|  | 124           instr->DeleteAndReplaceWith(NULL); | 
|  | 125           removed_++; | 
|  | 126         } else { | 
|  | 127           // Only one map survives the check. | 
|  | 128           maps->Clear(); | 
|  | 129           maps->Add(map, zone_); | 
|  | 130         } | 
|  | 131       } | 
|  | 132     } else { | 
|  | 133       // No prior information. | 
|  | 134       Insert(object, map); | 
|  | 135     } | 
|  | 136   } | 
|  | 137 | 
|  | 138   void ReduceStoreNamedField(HStoreNamedField* instr) { | 
|  | 139     HValue* object = instr->object()->ActualValue(); | 
|  | 140     if (instr->has_transition()) { | 
|  | 141       // This store transitions the object to a new map. | 
|  | 142       Kill(object); | 
|  | 143       Insert(object, MapConstant(instr->transition())); | 
|  | 144     } else if (IsMapAccess(instr->access())) { | 
|  | 145       // This is a store directly to the map field of the object. | 
|  | 146       Kill(object); | 
|  | 147       if (!instr->value()->IsConstant()) return; | 
|  | 148       Insert(object, MapConstant(instr->value())); | 
|  | 149     } else if (instr->CheckGVNFlag(kChangesMaps)) { | 
|  | 150       // This store indirectly changes the map of the object. | 
|  | 151       Kill(instr->object()); | 
|  | 152       UNREACHABLE(); | 
|  | 153     } | 
|  | 154   } | 
|  | 155 | 
|  | 156   void ReduceCompareMap(HCompareMap* instr) { | 
|  | 157     MapSet maps = FindMaps(instr->value()->ActualValue()); | 
|  | 158     if (maps == NULL) return; | 
|  | 159     if (maps->Contains(instr->map())) { | 
|  | 160       // TODO(titzer): replace with goto true branch | 
|  | 161       if (maps->size() == 1) compares_true_++; | 
|  | 162     } else { | 
|  | 163       // TODO(titzer): replace with goto false branch | 
|  | 164       compares_false_++; | 
|  | 165     } | 
|  | 166   } | 
|  | 167 | 
|  | 168   void ReduceTransitionElementsKind(HTransitionElementsKind* instr) { | 
|  | 169     MapSet maps = FindMaps(instr->object()->ActualValue()); | 
|  | 170     // Can only learn more about an object that already has a known set of maps. | 
|  | 171     if (maps == NULL) return; | 
|  | 172     if (maps->Contains(instr->original_map())) { | 
|  | 173       // If the object has the original map, it will be transitioned. | 
|  | 174       maps->Remove(instr->original_map()); | 
|  | 175       maps->Add(instr->transitioned_map(), zone_); | 
|  | 176     } else { | 
|  | 177       // Object does not have the given map, thus the transition is redundant. | 
|  | 178       instr->DeleteAndReplaceWith(instr->object()); | 
|  | 179       transitions_++; | 
|  | 180     } | 
|  | 181   } | 
|  | 182 | 
|  | 183   // Kill everything in the table. | 
|  | 184   void Kill() { | 
|  | 185     memset(objects_, 0, sizeof(objects_)); | 
|  | 186   } | 
|  | 187 | 
|  | 188   // Kill everything in the table that may alias {object}. | 
|  | 189   void Kill(HValue* object) { | 
|  | 190     for (int i = 0; i < kMaxTrackedObjects; i++) { | 
|  | 191       if (objects_[i] == NULL) continue; | 
|  | 192       if (aliasing_.MayAlias(objects_[i], object)) objects_[i] = NULL; | 
|  | 193     } | 
|  | 194     ASSERT(Find(object) < 0); | 
|  | 195   } | 
|  | 196 | 
|  | 197   void Print() { | 
|  | 198     for (int i = 0; i < kMaxTrackedObjects; i++) { | 
|  | 199       if (objects_[i] == NULL) continue; | 
|  | 200       PrintF("  checkmaps-table @%d: object #%d ", i, objects_[i]->id()); | 
|  | 201       if (checks_[i] != NULL) { | 
|  | 202         PrintF("check #%d ", checks_[i]->id()); | 
|  | 203       } | 
|  | 204       MapSet list = known_maps_[i]; | 
|  | 205       PrintF("%d maps { ", list->size()); | 
|  | 206       for (int j = 0; j < list->size(); j++) { | 
|  | 207         if (j > 0) PrintF(", "); | 
|  | 208         PrintF("%" V8PRIxPTR, list->at(j).Hashcode()); | 
|  | 209       } | 
|  | 210       PrintF(" }\n"); | 
|  | 211     } | 
|  | 212   } | 
|  | 213 | 
|  | 214   void PrintStats() { | 
|  | 215     if (redundant_ > 0)      PrintF("  redundant   = %2d\n", redundant_); | 
|  | 216     if (removed_ > 0)        PrintF("  removed     = %2d\n", removed_); | 
|  | 217     if (narrowed_ > 0)       PrintF("  narrowed    = %2d\n", narrowed_); | 
|  | 218     if (loads_ > 0)          PrintF("  loads       = %2d\n", loads_); | 
|  | 219     if (empty_ > 0)          PrintF("  empty       = %2d\n", empty_); | 
|  | 220     if (compares_true_ > 0)  PrintF("  cmp_true    = %2d\n", compares_true_); | 
|  | 221     if (compares_false_ > 0) PrintF("  cmp_false   = %2d\n", compares_false_); | 
|  | 222     if (transitions_ > 0)    PrintF("  transitions = %2d\n", transitions_); | 
|  | 223   } | 
|  | 224 | 
|  | 225  private: | 
|  | 226   int Find(HValue* object) { | 
|  | 227     for (int i = 0; i < kMaxTrackedObjects; i++) { | 
|  | 228       if (objects_[i] == NULL) continue; | 
|  | 229       if (aliasing_.MustAlias(objects_[i], object)) return i; | 
|  | 230     } | 
|  | 231     return -1; | 
|  | 232   } | 
|  | 233 | 
|  | 234   MapSet FindMaps(HValue* object) { | 
|  | 235     int index = Find(object); | 
|  | 236     return index < 0 ? NULL : known_maps_[index]; | 
|  | 237   } | 
|  | 238 | 
|  | 239   void Insert(HValue* object, Unique<Map> map) { | 
|  | 240     MapSet list = new(zone_) UniqueSet<Map>(); | 
|  | 241     list->Add(map, zone_); | 
|  | 242     Insert(object, NULL, list); | 
|  | 243   } | 
|  | 244 | 
|  | 245   void Insert(HValue* object, HCheckMaps* check, MapSet maps) { | 
|  | 246     for (int i = 0; i < kMaxTrackedObjects; i++) { | 
|  | 247       // TODO(titzer): drop old entries instead of disallowing new ones. | 
|  | 248       if (objects_[i] == NULL) { | 
|  | 249         objects_[i] = object; | 
|  | 250         checks_[i] = check; | 
|  | 251         known_maps_[i] = maps; | 
|  | 252         return; | 
|  | 253       } | 
|  | 254     } | 
|  | 255   } | 
|  | 256 | 
|  | 257   bool IsMapAccess(HObjectAccess access) { | 
|  | 258     return access.IsInobject() && access.offset() == JSObject::kMapOffset; | 
|  | 259   } | 
|  | 260 | 
|  | 261   Unique<Map> MapConstant(HValue* value) { | 
|  | 262     return Unique<Map>::cast(HConstant::cast(value)->GetUnique()); | 
|  | 263   } | 
|  | 264 | 
|  | 265   Zone* zone_; | 
|  | 266   HValue* objects_[kMaxTrackedObjects]; | 
|  | 267   HValue* checks_[kMaxTrackedObjects]; | 
|  | 268   MapSet known_maps_[kMaxTrackedObjects]; | 
|  | 269   HAliasAnalyzer aliasing_; | 
|  | 270   int redundant_; | 
|  | 271   int removed_; | 
|  | 272   int narrowed_; | 
|  | 273   int loads_; | 
|  | 274   int empty_; | 
|  | 275   int compares_true_; | 
|  | 276   int compares_false_; | 
|  | 277   int transitions_; | 
|  | 278 }; | 
|  | 279 | 
|  | 280 | 
|  | 281 void HCheckEliminationPhase::Run() { | 
|  | 282   for (int i = 0; i < graph()->blocks()->length(); i++) { | 
|  | 283     EliminateLocalChecks(graph()->blocks()->at(i)); | 
|  | 284   } | 
|  | 285 } | 
|  | 286 | 
|  | 287 | 
|  | 288 // For code de-uglification. | 
|  | 289 #define TRACE(x) if (FLAG_trace_check_elimination) PrintF x | 
|  | 290 | 
|  | 291 | 
|  | 292 // Eliminate checks local to a block. | 
|  | 293 void HCheckEliminationPhase::EliminateLocalChecks(HBasicBlock* block) { | 
|  | 294   HCheckTable table(zone()); | 
|  | 295   TRACE(("-- check-elim B%d ------------------------------------------------\n", | 
|  | 296          block->block_id())); | 
|  | 297 | 
|  | 298   for (HInstructionIterator it(block); !it.Done(); it.Advance()) { | 
|  | 299     bool changed = false; | 
|  | 300     HInstruction* instr = it.Current(); | 
|  | 301 | 
|  | 302     switch (instr->opcode()) { | 
|  | 303       case HValue::kCheckMaps: { | 
|  | 304         table.ReduceCheckMaps(HCheckMaps::cast(instr)); | 
|  | 305         changed = true; | 
|  | 306         break; | 
|  | 307       } | 
|  | 308       case HValue::kCheckValue: { | 
|  | 309         table.ReduceCheckValue(HCheckValue::cast(instr)); | 
|  | 310         changed = true; | 
|  | 311         break; | 
|  | 312       } | 
|  | 313       case HValue::kLoadNamedField: { | 
|  | 314         table.ReduceLoadNamedField(HLoadNamedField::cast(instr)); | 
|  | 315         changed = true; | 
|  | 316         break; | 
|  | 317       } | 
|  | 318       case HValue::kStoreNamedField: { | 
|  | 319         table.ReduceStoreNamedField(HStoreNamedField::cast(instr)); | 
|  | 320         changed = true; | 
|  | 321         break; | 
|  | 322       } | 
|  | 323       case HValue::kCompareMap: { | 
|  | 324         table.ReduceCompareMap(HCompareMap::cast(instr)); | 
|  | 325         changed = true; | 
|  | 326         break; | 
|  | 327       } | 
|  | 328       case HValue::kTransitionElementsKind: { | 
|  | 329         table.ReduceTransitionElementsKind( | 
|  | 330             HTransitionElementsKind::cast(instr)); | 
|  | 331         changed = true; | 
|  | 332         break; | 
|  | 333       } | 
|  | 334       case HValue::kCheckMapValue: { | 
|  | 335         table.ReduceCheckMapValue(HCheckMapValue::cast(instr)); | 
|  | 336         changed = true; | 
|  | 337         break; | 
|  | 338       } | 
|  | 339       default: { | 
|  | 340         // If the instruction changes maps uncontrollably, kill the whole town. | 
|  | 341         if (instr->CheckGVNFlag(kChangesMaps)) { | 
|  | 342           table.Kill(); | 
|  | 343           changed = true; | 
|  | 344         } | 
|  | 345       } | 
|  | 346       // Improvements possible: | 
|  | 347       // - eliminate HCheckSmi and HCheckHeapObject | 
|  | 348     } | 
|  | 349 | 
|  | 350     if (changed && FLAG_trace_check_elimination) table.Print(); | 
|  | 351   } | 
|  | 352 | 
|  | 353   if (FLAG_trace_check_elimination) table.PrintStats(); | 
|  | 354 } | 
|  | 355 | 
|  | 356 | 
|  | 357 } }  // namespace v8::internal | 
| OLD | NEW | 
|---|