| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "src/hydrogen-escape-analysis.h" | |
| 6 | |
| 7 namespace v8 { | |
| 8 namespace internal { | |
| 9 | |
| 10 | |
| 11 bool HEscapeAnalysisPhase::HasNoEscapingUses(HValue* value, int size) { | |
| 12 for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) { | |
| 13 HValue* use = it.value(); | |
| 14 if (use->HasEscapingOperandAt(it.index())) { | |
| 15 if (FLAG_trace_escape_analysis) { | |
| 16 PrintF("#%d (%s) escapes through #%d (%s) @%d\n", value->id(), | |
| 17 value->Mnemonic(), use->id(), use->Mnemonic(), it.index()); | |
| 18 } | |
| 19 return false; | |
| 20 } | |
| 21 if (use->HasOutOfBoundsAccess(size)) { | |
| 22 if (FLAG_trace_escape_analysis) { | |
| 23 PrintF("#%d (%s) out of bounds at #%d (%s) @%d\n", value->id(), | |
| 24 value->Mnemonic(), use->id(), use->Mnemonic(), it.index()); | |
| 25 } | |
| 26 return false; | |
| 27 } | |
| 28 int redefined_index = use->RedefinedOperandIndex(); | |
| 29 if (redefined_index == it.index() && !HasNoEscapingUses(use, size)) { | |
| 30 if (FLAG_trace_escape_analysis) { | |
| 31 PrintF("#%d (%s) escapes redefinition #%d (%s) @%d\n", value->id(), | |
| 32 value->Mnemonic(), use->id(), use->Mnemonic(), it.index()); | |
| 33 } | |
| 34 return false; | |
| 35 } | |
| 36 } | |
| 37 return true; | |
| 38 } | |
| 39 | |
| 40 | |
| 41 void HEscapeAnalysisPhase::CollectCapturedValues() { | |
| 42 int block_count = graph()->blocks()->length(); | |
| 43 for (int i = 0; i < block_count; ++i) { | |
| 44 HBasicBlock* block = graph()->blocks()->at(i); | |
| 45 for (HInstructionIterator it(block); !it.Done(); it.Advance()) { | |
| 46 HInstruction* instr = it.Current(); | |
| 47 if (!instr->IsAllocate()) continue; | |
| 48 HAllocate* allocate = HAllocate::cast(instr); | |
| 49 if (!allocate->size()->IsInteger32Constant()) continue; | |
| 50 int size_in_bytes = allocate->size()->GetInteger32Constant(); | |
| 51 if (HasNoEscapingUses(instr, size_in_bytes)) { | |
| 52 if (FLAG_trace_escape_analysis) { | |
| 53 PrintF("#%d (%s) is being captured\n", instr->id(), | |
| 54 instr->Mnemonic()); | |
| 55 } | |
| 56 captured_.Add(instr, zone()); | |
| 57 } | |
| 58 } | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 | |
| 63 HCapturedObject* HEscapeAnalysisPhase::NewState(HInstruction* previous) { | |
| 64 Zone* zone = graph()->zone(); | |
| 65 HCapturedObject* state = | |
| 66 new(zone) HCapturedObject(number_of_values_, number_of_objects_, zone); | |
| 67 state->InsertAfter(previous); | |
| 68 return state; | |
| 69 } | |
| 70 | |
| 71 | |
| 72 // Create a new state for replacing HAllocate instructions. | |
| 73 HCapturedObject* HEscapeAnalysisPhase::NewStateForAllocation( | |
| 74 HInstruction* previous) { | |
| 75 HConstant* undefined = graph()->GetConstantUndefined(); | |
| 76 HCapturedObject* state = NewState(previous); | |
| 77 for (int index = 0; index < number_of_values_; index++) { | |
| 78 state->SetOperandAt(index, undefined); | |
| 79 } | |
| 80 return state; | |
| 81 } | |
| 82 | |
| 83 | |
| 84 // Create a new state full of phis for loop header entries. | |
| 85 HCapturedObject* HEscapeAnalysisPhase::NewStateForLoopHeader( | |
| 86 HInstruction* previous, | |
| 87 HCapturedObject* old_state) { | |
| 88 HBasicBlock* block = previous->block(); | |
| 89 HCapturedObject* state = NewState(previous); | |
| 90 for (int index = 0; index < number_of_values_; index++) { | |
| 91 HValue* operand = old_state->OperandAt(index); | |
| 92 HPhi* phi = NewPhiAndInsert(block, operand, index); | |
| 93 state->SetOperandAt(index, phi); | |
| 94 } | |
| 95 return state; | |
| 96 } | |
| 97 | |
| 98 | |
| 99 // Create a new state by copying an existing one. | |
| 100 HCapturedObject* HEscapeAnalysisPhase::NewStateCopy( | |
| 101 HInstruction* previous, | |
| 102 HCapturedObject* old_state) { | |
| 103 HCapturedObject* state = NewState(previous); | |
| 104 for (int index = 0; index < number_of_values_; index++) { | |
| 105 HValue* operand = old_state->OperandAt(index); | |
| 106 state->SetOperandAt(index, operand); | |
| 107 } | |
| 108 return state; | |
| 109 } | |
| 110 | |
| 111 | |
| 112 // Insert a newly created phi into the given block and fill all incoming | |
| 113 // edges with the given value. | |
| 114 HPhi* HEscapeAnalysisPhase::NewPhiAndInsert(HBasicBlock* block, | |
| 115 HValue* incoming_value, | |
| 116 int index) { | |
| 117 Zone* zone = graph()->zone(); | |
| 118 HPhi* phi = new(zone) HPhi(HPhi::kInvalidMergedIndex, zone); | |
| 119 for (int i = 0; i < block->predecessors()->length(); i++) { | |
| 120 phi->AddInput(incoming_value); | |
| 121 } | |
| 122 block->AddPhi(phi); | |
| 123 return phi; | |
| 124 } | |
| 125 | |
| 126 | |
| 127 // Insert a newly created value check as a replacement for map checks. | |
| 128 HValue* HEscapeAnalysisPhase::NewMapCheckAndInsert(HCapturedObject* state, | |
| 129 HCheckMaps* mapcheck) { | |
| 130 Zone* zone = graph()->zone(); | |
| 131 HValue* value = state->map_value(); | |
| 132 // TODO(mstarzinger): This will narrow a map check against a set of maps | |
| 133 // down to the first element in the set. Revisit and fix this. | |
| 134 HCheckValue* check = HCheckValue::New(graph()->isolate(), zone, NULL, value, | |
| 135 mapcheck->maps()->at(0), false); | |
| 136 check->InsertBefore(mapcheck); | |
| 137 return check; | |
| 138 } | |
| 139 | |
| 140 | |
| 141 // Replace a field load with a given value, forcing Smi representation if | |
| 142 // necessary. | |
| 143 HValue* HEscapeAnalysisPhase::NewLoadReplacement( | |
| 144 HLoadNamedField* load, HValue* load_value) { | |
| 145 HValue* replacement = load_value; | |
| 146 Representation representation = load->representation(); | |
| 147 if (representation.IsSmiOrInteger32() || representation.IsDouble()) { | |
| 148 Zone* zone = graph()->zone(); | |
| 149 HInstruction* new_instr = HForceRepresentation::New( | |
| 150 graph()->isolate(), zone, NULL, load_value, representation); | |
| 151 new_instr->InsertAfter(load); | |
| 152 replacement = new_instr; | |
| 153 } | |
| 154 return replacement; | |
| 155 } | |
| 156 | |
| 157 | |
| 158 // Performs a forward data-flow analysis of all loads and stores on the | |
| 159 // given captured allocation. This uses a reverse post-order iteration | |
| 160 // over affected basic blocks. All non-escaping instructions are handled | |
| 161 // and replaced during the analysis. | |
| 162 void HEscapeAnalysisPhase::AnalyzeDataFlow(HInstruction* allocate) { | |
| 163 HBasicBlock* allocate_block = allocate->block(); | |
| 164 block_states_.AddBlock(NULL, graph()->blocks()->length(), zone()); | |
| 165 | |
| 166 // Iterate all blocks starting with the allocation block, since the | |
| 167 // allocation cannot dominate blocks that come before. | |
| 168 int start = allocate_block->block_id(); | |
| 169 for (int i = start; i < graph()->blocks()->length(); i++) { | |
| 170 HBasicBlock* block = graph()->blocks()->at(i); | |
| 171 HCapturedObject* state = StateAt(block); | |
| 172 | |
| 173 // Skip blocks that are not dominated by the captured allocation. | |
| 174 if (!allocate_block->Dominates(block) && allocate_block != block) continue; | |
| 175 if (FLAG_trace_escape_analysis) { | |
| 176 PrintF("Analyzing data-flow in B%d\n", block->block_id()); | |
| 177 } | |
| 178 | |
| 179 // Go through all instructions of the current block. | |
| 180 for (HInstructionIterator it(block); !it.Done(); it.Advance()) { | |
| 181 HInstruction* instr = it.Current(); | |
| 182 switch (instr->opcode()) { | |
| 183 case HValue::kAllocate: { | |
| 184 if (instr != allocate) continue; | |
| 185 state = NewStateForAllocation(allocate); | |
| 186 break; | |
| 187 } | |
| 188 case HValue::kLoadNamedField: { | |
| 189 HLoadNamedField* load = HLoadNamedField::cast(instr); | |
| 190 int index = load->access().offset() / kPointerSize; | |
| 191 if (load->object() != allocate) continue; | |
| 192 DCHECK(load->access().IsInobject()); | |
| 193 HValue* replacement = | |
| 194 NewLoadReplacement(load, state->OperandAt(index)); | |
| 195 load->DeleteAndReplaceWith(replacement); | |
| 196 if (FLAG_trace_escape_analysis) { | |
| 197 PrintF("Replacing load #%d with #%d (%s)\n", load->id(), | |
| 198 replacement->id(), replacement->Mnemonic()); | |
| 199 } | |
| 200 break; | |
| 201 } | |
| 202 case HValue::kStoreNamedField: { | |
| 203 HStoreNamedField* store = HStoreNamedField::cast(instr); | |
| 204 int index = store->access().offset() / kPointerSize; | |
| 205 if (store->object() != allocate) continue; | |
| 206 DCHECK(store->access().IsInobject()); | |
| 207 state = NewStateCopy(store->previous(), state); | |
| 208 state->SetOperandAt(index, store->value()); | |
| 209 if (store->has_transition()) { | |
| 210 state->SetOperandAt(0, store->transition()); | |
| 211 } | |
| 212 if (store->HasObservableSideEffects()) { | |
| 213 state->ReuseSideEffectsFromStore(store); | |
| 214 } | |
| 215 store->DeleteAndReplaceWith(store->ActualValue()); | |
| 216 if (FLAG_trace_escape_analysis) { | |
| 217 PrintF("Replacing store #%d%s\n", instr->id(), | |
| 218 store->has_transition() ? " (with transition)" : ""); | |
| 219 } | |
| 220 break; | |
| 221 } | |
| 222 case HValue::kArgumentsObject: | |
| 223 case HValue::kCapturedObject: | |
| 224 case HValue::kSimulate: { | |
| 225 for (int i = 0; i < instr->OperandCount(); i++) { | |
| 226 if (instr->OperandAt(i) != allocate) continue; | |
| 227 instr->SetOperandAt(i, state); | |
| 228 } | |
| 229 break; | |
| 230 } | |
| 231 case HValue::kCheckHeapObject: { | |
| 232 HCheckHeapObject* check = HCheckHeapObject::cast(instr); | |
| 233 if (check->value() != allocate) continue; | |
| 234 check->DeleteAndReplaceWith(check->ActualValue()); | |
| 235 break; | |
| 236 } | |
| 237 case HValue::kCheckMaps: { | |
| 238 HCheckMaps* mapcheck = HCheckMaps::cast(instr); | |
| 239 if (mapcheck->value() != allocate) continue; | |
| 240 NewMapCheckAndInsert(state, mapcheck); | |
| 241 mapcheck->DeleteAndReplaceWith(mapcheck->ActualValue()); | |
| 242 break; | |
| 243 } | |
| 244 default: | |
| 245 // Nothing to see here, move along ... | |
| 246 break; | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 // Propagate the block state forward to all successor blocks. | |
| 251 for (int i = 0; i < block->end()->SuccessorCount(); i++) { | |
| 252 HBasicBlock* succ = block->end()->SuccessorAt(i); | |
| 253 if (!allocate_block->Dominates(succ)) continue; | |
| 254 if (succ->predecessors()->length() == 1) { | |
| 255 // Case 1: This is the only predecessor, just reuse state. | |
| 256 SetStateAt(succ, state); | |
| 257 } else if (StateAt(succ) == NULL && succ->IsLoopHeader()) { | |
| 258 // Case 2: This is a state that enters a loop header, be | |
| 259 // pessimistic about loop headers, add phis for all values. | |
| 260 SetStateAt(succ, NewStateForLoopHeader(succ->first(), state)); | |
| 261 } else if (StateAt(succ) == NULL) { | |
| 262 // Case 3: This is the first state propagated forward to the | |
| 263 // successor, leave a copy of the current state. | |
| 264 SetStateAt(succ, NewStateCopy(succ->first(), state)); | |
| 265 } else { | |
| 266 // Case 4: This is a state that needs merging with previously | |
| 267 // propagated states, potentially introducing new phis lazily or | |
| 268 // adding values to existing phis. | |
| 269 HCapturedObject* succ_state = StateAt(succ); | |
| 270 for (int index = 0; index < number_of_values_; index++) { | |
| 271 HValue* operand = state->OperandAt(index); | |
| 272 HValue* succ_operand = succ_state->OperandAt(index); | |
| 273 if (succ_operand->IsPhi() && succ_operand->block() == succ) { | |
| 274 // Phi already exists, add operand. | |
| 275 HPhi* phi = HPhi::cast(succ_operand); | |
| 276 phi->SetOperandAt(succ->PredecessorIndexOf(block), operand); | |
| 277 } else if (succ_operand != operand) { | |
| 278 // Phi does not exist, introduce one. | |
| 279 HPhi* phi = NewPhiAndInsert(succ, succ_operand, index); | |
| 280 phi->SetOperandAt(succ->PredecessorIndexOf(block), operand); | |
| 281 succ_state->SetOperandAt(index, phi); | |
| 282 } | |
| 283 } | |
| 284 } | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 // All uses have been handled. | |
| 289 DCHECK(allocate->HasNoUses()); | |
| 290 allocate->DeleteAndReplaceWith(NULL); | |
| 291 } | |
| 292 | |
| 293 | |
| 294 void HEscapeAnalysisPhase::PerformScalarReplacement() { | |
| 295 for (int i = 0; i < captured_.length(); i++) { | |
| 296 HAllocate* allocate = HAllocate::cast(captured_.at(i)); | |
| 297 | |
| 298 // Compute number of scalar values and start with clean slate. | |
| 299 int size_in_bytes = allocate->size()->GetInteger32Constant(); | |
| 300 number_of_values_ = size_in_bytes / kPointerSize; | |
| 301 number_of_objects_++; | |
| 302 block_states_.Rewind(0); | |
| 303 | |
| 304 // Perform actual analysis step. | |
| 305 AnalyzeDataFlow(allocate); | |
| 306 | |
| 307 cumulative_values_ += number_of_values_; | |
| 308 DCHECK(allocate->HasNoUses()); | |
| 309 DCHECK(!allocate->IsLinked()); | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 | |
| 314 void HEscapeAnalysisPhase::Run() { | |
| 315 // TODO(mstarzinger): We disable escape analysis with OSR for now, because | |
| 316 // spill slots might be uninitialized. Needs investigation. | |
| 317 if (graph()->has_osr()) return; | |
| 318 int max_fixpoint_iteration_count = FLAG_escape_analysis_iterations; | |
| 319 for (int i = 0; i < max_fixpoint_iteration_count; i++) { | |
| 320 CollectCapturedValues(); | |
| 321 if (captured_.is_empty()) break; | |
| 322 PerformScalarReplacement(); | |
| 323 captured_.Rewind(0); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 | |
| 328 } // namespace internal | |
| 329 } // namespace v8 | |
| OLD | NEW |