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