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-alias-analysis.h" | |
| 29 #include "hydrogen-load-elimination.h" | |
| 30 #include "hydrogen-instructions.h" | |
| 31 | |
| 32 namespace v8 { | |
| 33 namespace internal { | |
| 34 | |
| 35 static const int kMaxTrackedFields = 16; | |
| 36 static const int kMaxTrackedObjects = 5; | |
| 37 | |
| 38 // An element in the field approximation list. | |
| 39 class HFieldApprox : public ZoneObject { | |
|
Michael Starzinger
2013/09/12 20:04:25
nit: Can we use the full name HFieldApproximation?
| |
| 40 public: // Just a data blob. | |
| 41 HValue* object_; | |
| 42 HLoadNamedField* last_load_; | |
| 43 HValue* last_value_; | |
| 44 HFieldApprox *next_; | |
|
Michael Starzinger
2013/09/12 20:04:25
nit: Asterisk sticks to the left.
| |
| 45 }; | |
| 46 | |
| 47 | |
| 48 // The main datastructure used during load/store elimination. Each in-object | |
| 49 // field is tracked separately. For each field, store a list of known field | |
| 50 // values for known objects. | |
| 51 class HLoadElimTable { | |
|
Michael Starzinger
2013/09/12 20:04:25
nit: Can we use the full name HLoadEliminationTabl
| |
| 52 public: | |
| 53 HLoadElimTable(Zone* zone, HAliasAnalyzer* aliasing) | |
| 54 : zone_(zone), fields_(kMaxTrackedFields, zone), aliasing_(aliasing) { } | |
| 55 | |
| 56 // Process a load instruction, updating internal table state. If a previous | |
| 57 // load or store for this object and field exists, return the new value with | |
| 58 // which the load should be replaced. Otherwise, return {instr}. | |
| 59 HValue* load(HLoadNamedField* instr) { | |
| 60 int field = FieldOf(instr->access()); | |
| 61 if (field < 0) return instr; | |
| 62 | |
| 63 HValue* object = aliasing_->ActualValue(instr->object()); | |
| 64 HFieldApprox* approx = FindOrCreate(object, field); | |
| 65 | |
| 66 if (approx->last_value_ == NULL) { | |
| 67 // Load is not redundant. Fill out a new entry. | |
| 68 approx->last_load_ = instr; | |
| 69 approx->last_value_ = instr; | |
| 70 return instr; | |
| 71 } else { | |
| 72 // Eliminate the load. Reuse previously stored value or load instruction. | |
| 73 return approx->last_value_; | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 // Process a store instruction, updating internal table state. If a previous | |
| 78 // store to the same object and field makes this store redundant (e.g. because | |
| 79 // the stored values are the same), return NULL indicating that this store | |
| 80 // instruction is redundant. Otherwise, return {instr}. | |
| 81 HValue* store(HStoreNamedField* instr) { | |
| 82 int field = FieldOf(instr->access()); | |
| 83 if (field < 0) return instr; | |
| 84 | |
| 85 HValue* object = aliasing_->ActualValue(instr->object()); | |
| 86 HValue* value = instr->value(); | |
| 87 | |
| 88 // Kill non-equivalent may-alias entries. | |
| 89 KillFieldInternal(object, field, value); | |
| 90 HFieldApprox* approx = FindOrCreate(object, field); | |
| 91 | |
| 92 if (approx->last_value_ != value) { | |
| 93 // The store is not redundant. Update the entry. | |
| 94 approx->last_load_ = NULL; | |
| 95 approx->last_value_ = value; | |
| 96 return instr; | |
| 97 } else { | |
| 98 // The store is redundant because the field already has this value. | |
| 99 return NULL; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 // Kill everything in this table. | |
| 104 void Kill() { | |
| 105 fields_.Rewind(0); | |
| 106 } | |
| 107 | |
| 108 // Kill everything potentially affected by the given store. | |
| 109 void Kill(HStoreNamedField* store) { | |
|
Michael Starzinger
2013/09/12 20:04:25
This method is never called, let's drop it.
| |
| 110 int field = FieldOf(store->access()); | |
| 111 if (field >= 0 && field < fields_.length()) { | |
| 112 HValue* object = aliasing_->ActualValue(store->object()); | |
| 113 KillFieldInternal(object, field, store->value()); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 // Find or create an entry for the given object and field pair. | |
| 118 HFieldApprox* FindOrCreate(HValue* object, int field) { | |
| 119 EnsureFields(field + 1); | |
| 120 | |
| 121 // Search for a field approximation for this object. | |
| 122 HFieldApprox* approx = fields_[field]; | |
| 123 int count = 0; | |
| 124 while (approx != NULL) { | |
| 125 if (aliasing_->MustAlias(object, approx->object_)) return approx; | |
| 126 count++; | |
| 127 approx = approx->next_; | |
| 128 } | |
| 129 | |
| 130 if (count >= kMaxTrackedObjects) { | |
| 131 // Pull the last entry off the end and repurpose it for this object. | |
| 132 approx = ReuseLastApprox(field); | |
| 133 } else { | |
| 134 // Allocate a new entry. | |
| 135 approx = new(zone_) HFieldApprox(); | |
| 136 } | |
| 137 | |
| 138 // Insert the entry at the head of the list. | |
| 139 approx->object_ = object; | |
| 140 approx->last_load_ = NULL; | |
| 141 approx->last_value_ = NULL; | |
| 142 approx->next_ = fields_[field]; | |
| 143 fields_[field] = approx; | |
| 144 | |
| 145 return approx; | |
| 146 } | |
| 147 | |
| 148 // Kill all entries for a given field that _may_ alias the given object | |
| 149 // and do _not_ have the given value. | |
| 150 void KillFieldInternal(HValue* object, int field, HValue* value) { | |
| 151 if (field >= fields_.length()) return; // Nothing to do. | |
| 152 | |
| 153 HFieldApprox* approx = fields_[field]; | |
| 154 HFieldApprox* prev = NULL; | |
| 155 while (approx != NULL) { | |
| 156 if (aliasing_->MayAlias(object, approx->object_)) { | |
| 157 if (approx->last_value_ != value) { | |
| 158 // Kill an aliasing entry that doesn't agree on the value. | |
| 159 if (prev != NULL) { | |
| 160 prev->next_ = approx->next_; | |
| 161 } else { | |
| 162 fields_[field] = approx->next_; | |
| 163 } | |
| 164 approx = approx->next_; | |
| 165 continue; | |
| 166 } | |
| 167 } | |
| 168 prev = approx; | |
| 169 approx = approx->next_; | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 // Remove the last approximation for a field so that it can be reused. | |
| 174 // We reuse the last entry because it was the first inserted and is | |
| 175 // thus farthest away from the current instruction. | |
| 176 HFieldApprox* ReuseLastApprox(int field) { | |
| 177 HFieldApprox* approx = fields_[field]; | |
| 178 ASSERT(approx != NULL); | |
| 179 | |
| 180 HFieldApprox* prev = NULL; | |
| 181 while (approx->next_ != NULL) { | |
| 182 prev = approx; | |
| 183 approx = approx->next_; | |
| 184 } | |
| 185 if (prev != NULL) prev->next_ = NULL; | |
| 186 return approx; | |
| 187 } | |
| 188 | |
| 189 // Ensure internal storage for the given number of fields. | |
| 190 void EnsureFields(int num_fields) { | |
| 191 while (fields_.length() < num_fields) fields_.Add(NULL, zone_); | |
| 192 } | |
| 193 | |
| 194 // Compute the field index for the given object access; -1 if not tracked. | |
| 195 int FieldOf(HObjectAccess access) { | |
| 196 // Only track kMaxTrackedFields in-object fields. | |
| 197 if (!access.IsInobject()) return -1; | |
| 198 int offset = access.offset(); | |
| 199 if (offset >= kMaxTrackedFields * kPointerSize) return -1; | |
| 200 ASSERT((offset % kPointerSize) == 0); // Assume aligned accesses. | |
| 201 return offset / kPointerSize; | |
| 202 } | |
| 203 | |
| 204 // Print this table to stdout. | |
| 205 void Print() { | |
| 206 for (int i = 0; i < fields_.length(); i++) { | |
| 207 PrintF(" field %d: ", i); | |
| 208 for (HFieldApprox* a = fields_[i]; a != NULL; a = a->next_) { | |
| 209 PrintF("[o%d =", a->object_->id()); | |
| 210 if (a->last_load_ != NULL) PrintF(" L%d", a->last_load_->id()); | |
| 211 if (a->last_value_ != NULL) PrintF(" v%d", a->last_value_->id()); | |
| 212 PrintF("] "); | |
| 213 } | |
| 214 PrintF("\n"); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 Zone* zone_; | |
|
Michael Starzinger
2013/09/12 20:04:25
nit: Make the field private. Maybe even make the i
| |
| 219 ZoneList<HFieldApprox*> fields_; | |
| 220 HAliasAnalyzer* aliasing_; | |
| 221 }; | |
| 222 | |
| 223 | |
| 224 void HLoadEliminationPhase::Run() { | |
| 225 for (int i = 0; i < graph()->blocks()->length(); i++) { | |
| 226 HBasicBlock* block = graph()->blocks()->at(i); | |
| 227 EliminateLoads(block); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 | |
| 232 // For code de-uglification. | |
| 233 #define TRACE(x) if (FLAG_trace_load_elimination) PrintF x | |
| 234 | |
| 235 | |
| 236 // Eliminate loads and stores local to a block. | |
| 237 void HLoadEliminationPhase::EliminateLoads(HBasicBlock* block) { | |
| 238 HAliasAnalyzer aliasing; | |
| 239 HLoadElimTable table(zone(), &aliasing); | |
| 240 | |
| 241 TRACE(("-- load-elim B%d -------------------------------------------------\n", | |
| 242 block->block_id())); | |
| 243 | |
| 244 for (HInstructionIterator it(block); !it.Done(); it.Advance()) { | |
| 245 bool changed = false; | |
| 246 HInstruction* instr = it.Current(); | |
| 247 | |
| 248 switch (instr->opcode()) { | |
| 249 case HValue::kLoadNamedField: { | |
|
Michael Starzinger
2013/09/12 20:04:25
style: Indent cases by one level.
| |
| 250 HLoadNamedField* load = HLoadNamedField::cast(instr); | |
| 251 TRACE((" process L%d field %d (o%d)\n", | |
| 252 instr->id(), | |
| 253 table.FieldOf(load->access()), | |
| 254 table.aliasing_->ActualValue(load->object())->id())); | |
| 255 HValue* result = table.load(load); | |
| 256 if (result != instr) { | |
| 257 // The load can be replaced with a previous load or a value. | |
| 258 TRACE((" replace L%d -> v%d\n", instr->id(), result->id())); | |
| 259 instr->DeleteAndReplaceWith(result); | |
| 260 } | |
| 261 changed = true; | |
| 262 break; | |
| 263 } | |
| 264 case HValue::kStoreNamedField: { | |
| 265 HStoreNamedField* store = HStoreNamedField::cast(instr); | |
| 266 TRACE((" process S%d field %d (o%d) = v%d\n", | |
| 267 instr->id(), | |
| 268 table.FieldOf(store->access()), | |
| 269 table.aliasing_->ActualValue(store->object())->id(), | |
| 270 store->value()->id())); | |
| 271 HValue* result = table.store(store); | |
|
Michael Starzinger
2013/09/12 20:04:25
An HStoreNamedField with a transition also changes
| |
| 272 if (result == NULL) { | |
| 273 // The store is redundant. Remove it. | |
| 274 TRACE((" remove S%d\n", instr->id())); | |
| 275 instr->DeleteAndReplaceWith(NULL); | |
| 276 } | |
| 277 changed = true; | |
| 278 break; | |
| 279 } | |
| 280 default: { | |
| 281 if (instr->CheckGVNFlag(kChangesInobjectFields)) { | |
| 282 TRACE((" kill-all i%d\n", instr->id())); | |
| 283 table.Kill(); | |
| 284 } | |
| 285 } | |
| 286 // Improvements possible: | |
| 287 // - learn from HCheckMaps for field 0 | |
| 288 // - remove unobservable stores (write-after-write) | |
| 289 } | |
| 290 | |
| 291 if (changed && FLAG_trace_load_elimination) { | |
| 292 table.Print(); | |
| 293 } | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 | |
| 298 } } // namespace v8::internal | |
| OLD | NEW |