hydrogen-escape-analysis.cc 12.1 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/hydrogen-escape-analysis.h"
6 7 8 9 10

namespace v8 {
namespace internal {


11
bool HEscapeAnalysisPhase::HasNoEscapingUses(HValue* value, int size) {
12
  for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
13 14 15
    HValue* use = it.value();
    if (use->HasEscapingOperandAt(it.index())) {
      if (FLAG_trace_escape_analysis) {
16 17
        PrintF("#%d (%s) escapes through #%d (%s) @%d\n", value->id(),
               value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
18
      }
19 20
      return false;
    }
21 22 23 24 25 26 27 28 29
    if (use->HasOutOfBoundsAccess(size)) {
      if (FLAG_trace_escape_analysis) {
        PrintF("#%d (%s) out of bounds at #%d (%s) @%d\n", value->id(),
               value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
      }
      return false;
    }
    int redefined_index = use->RedefinedOperandIndex();
    if (redefined_index == it.index() && !HasNoEscapingUses(use, size)) {
30 31 32 33 34
      if (FLAG_trace_escape_analysis) {
        PrintF("#%d (%s) escapes redefinition #%d (%s) @%d\n", value->id(),
               value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
      }
      return false;
35 36
    }
  }
37
  return true;
38 39 40
}


41 42
void HEscapeAnalysisPhase::CollectCapturedValues() {
  int block_count = graph()->blocks()->length();
43
  for (int i = 0; i < block_count; ++i) {
44
    HBasicBlock* block = graph()->blocks()->at(i);
45 46
    for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
      HInstruction* instr = it.Current();
47 48 49 50 51
      if (!instr->IsAllocate()) continue;
      HAllocate* allocate = HAllocate::cast(instr);
      if (!allocate->size()->IsInteger32Constant()) continue;
      int size_in_bytes = allocate->size()->GetInteger32Constant();
      if (HasNoEscapingUses(instr, size_in_bytes)) {
52 53 54 55 56
        if (FLAG_trace_escape_analysis) {
          PrintF("#%d (%s) is being captured\n", instr->id(),
                 instr->Mnemonic());
        }
        captured_.Add(instr, zone());
57 58 59 60 61 62
      }
    }
  }
}


63 64
HCapturedObject* HEscapeAnalysisPhase::NewState(HInstruction* previous) {
  Zone* zone = graph()->zone();
65 66
  HCapturedObject* state =
      new(zone) HCapturedObject(number_of_values_, number_of_objects_, zone);
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
  state->InsertAfter(previous);
  return state;
}


// Create a new state for replacing HAllocate instructions.
HCapturedObject* HEscapeAnalysisPhase::NewStateForAllocation(
    HInstruction* previous) {
  HConstant* undefined = graph()->GetConstantUndefined();
  HCapturedObject* state = NewState(previous);
  for (int index = 0; index < number_of_values_; index++) {
    state->SetOperandAt(index, undefined);
  }
  return state;
}


// Create a new state full of phis for loop header entries.
HCapturedObject* HEscapeAnalysisPhase::NewStateForLoopHeader(
86 87
    HInstruction* previous,
    HCapturedObject* old_state) {
88 89 90 91 92 93 94 95 96 97 98 99 100
  HBasicBlock* block = previous->block();
  HCapturedObject* state = NewState(previous);
  for (int index = 0; index < number_of_values_; index++) {
    HValue* operand = old_state->OperandAt(index);
    HPhi* phi = NewPhiAndInsert(block, operand, index);
    state->SetOperandAt(index, phi);
  }
  return state;
}


// Create a new state by copying an existing one.
HCapturedObject* HEscapeAnalysisPhase::NewStateCopy(
101 102
    HInstruction* previous,
    HCapturedObject* old_state) {
103 104 105 106 107 108 109 110 111 112
  HCapturedObject* state = NewState(previous);
  for (int index = 0; index < number_of_values_; index++) {
    HValue* operand = old_state->OperandAt(index);
    state->SetOperandAt(index, operand);
  }
  return state;
}


// Insert a newly created phi into the given block and fill all incoming
113
// edges with the given value.
114 115 116
HPhi* HEscapeAnalysisPhase::NewPhiAndInsert(HBasicBlock* block,
                                            HValue* incoming_value,
                                            int index) {
117
  Zone* zone = graph()->zone();
118
  HPhi* phi = new(zone) HPhi(HPhi::kInvalidMergedIndex, zone);
119 120 121 122 123 124 125 126
  for (int i = 0; i < block->predecessors()->length(); i++) {
    phi->AddInput(incoming_value);
  }
  block->AddPhi(phi);
  return phi;
}


127 128 129 130 131 132 133
// Insert a newly created value check as a replacement for map checks.
HValue* HEscapeAnalysisPhase::NewMapCheckAndInsert(HCapturedObject* state,
                                                   HCheckMaps* mapcheck) {
  Zone* zone = graph()->zone();
  HValue* value = state->map_value();
  // TODO(mstarzinger): This will narrow a map check against a set of maps
  // down to the first element in the set. Revisit and fix this.
134 135
  HCheckValue* check = HCheckValue::New(graph()->isolate(), zone, NULL, value,
                                        mapcheck->maps()->at(0), false);
136 137 138 139 140
  check->InsertBefore(mapcheck);
  return check;
}


141 142 143 144 145 146
// Replace a field load with a given value, forcing Smi representation if
// necessary.
HValue* HEscapeAnalysisPhase::NewLoadReplacement(
    HLoadNamedField* load, HValue* load_value) {
  HValue* replacement = load_value;
  Representation representation = load->representation();
147
  if (representation.IsSmiOrInteger32() || representation.IsDouble()) {
148
    Zone* zone = graph()->zone();
149 150
    HInstruction* new_instr = HForceRepresentation::New(
        graph()->isolate(), zone, NULL, load_value, representation);
151 152 153 154 155 156 157
    new_instr->InsertAfter(load);
    replacement = new_instr;
  }
  return replacement;
}


158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
// Performs a forward data-flow analysis of all loads and stores on the
// given captured allocation. This uses a reverse post-order iteration
// over affected basic blocks. All non-escaping instructions are handled
// and replaced during the analysis.
void HEscapeAnalysisPhase::AnalyzeDataFlow(HInstruction* allocate) {
  HBasicBlock* allocate_block = allocate->block();
  block_states_.AddBlock(NULL, graph()->blocks()->length(), zone());

  // Iterate all blocks starting with the allocation block, since the
  // allocation cannot dominate blocks that come before.
  int start = allocate_block->block_id();
  for (int i = start; i < graph()->blocks()->length(); i++) {
    HBasicBlock* block = graph()->blocks()->at(i);
    HCapturedObject* state = StateAt(block);

    // Skip blocks that are not dominated by the captured allocation.
    if (!allocate_block->Dominates(block) && allocate_block != block) continue;
    if (FLAG_trace_escape_analysis) {
      PrintF("Analyzing data-flow in B%d\n", block->block_id());
    }

    // Go through all instructions of the current block.
    for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
      HInstruction* instr = it.Current();
      switch (instr->opcode()) {
        case HValue::kAllocate: {
          if (instr != allocate) continue;
          state = NewStateForAllocation(allocate);
          break;
        }
        case HValue::kLoadNamedField: {
          HLoadNamedField* load = HLoadNamedField::cast(instr);
          int index = load->access().offset() / kPointerSize;
          if (load->object() != allocate) continue;
192
          DCHECK(load->access().IsInobject());
193 194
          HValue* replacement =
            NewLoadReplacement(load, state->OperandAt(index));
195 196
          load->DeleteAndReplaceWith(replacement);
          if (FLAG_trace_escape_analysis) {
197
            PrintF("Replacing load #%d with #%d (%s)\n", load->id(),
198 199 200 201 202 203 204 205
                   replacement->id(), replacement->Mnemonic());
          }
          break;
        }
        case HValue::kStoreNamedField: {
          HStoreNamedField* store = HStoreNamedField::cast(instr);
          int index = store->access().offset() / kPointerSize;
          if (store->object() != allocate) continue;
206
          DCHECK(store->access().IsInobject());
207
          state = NewStateCopy(store->previous(), state);
208
          state->SetOperandAt(index, store->value());
209 210 211
          if (store->has_transition()) {
            state->SetOperandAt(0, store->transition());
          }
212 213 214
          if (store->HasObservableSideEffects()) {
            state->ReuseSideEffectsFromStore(store);
          }
215
          store->DeleteAndReplaceWith(store->ActualValue());
216
          if (FLAG_trace_escape_analysis) {
217 218
            PrintF("Replacing store #%d%s\n", instr->id(),
                   store->has_transition() ? " (with transition)" : "");
219 220 221 222
          }
          break;
        }
        case HValue::kArgumentsObject:
223 224
        case HValue::kCapturedObject:
        case HValue::kSimulate: {
225 226 227 228 229 230 231 232 233
          for (int i = 0; i < instr->OperandCount(); i++) {
            if (instr->OperandAt(i) != allocate) continue;
            instr->SetOperandAt(i, state);
          }
          break;
        }
        case HValue::kCheckHeapObject: {
          HCheckHeapObject* check = HCheckHeapObject::cast(instr);
          if (check->value() != allocate) continue;
234
          check->DeleteAndReplaceWith(check->ActualValue());
235 236 237 238 239
          break;
        }
        case HValue::kCheckMaps: {
          HCheckMaps* mapcheck = HCheckMaps::cast(instr);
          if (mapcheck->value() != allocate) continue;
240
          NewMapCheckAndInsert(state, mapcheck);
241
          mapcheck->DeleteAndReplaceWith(mapcheck->ActualValue());
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
          break;
        }
        default:
          // Nothing to see here, move along ...
          break;
      }
    }

    // Propagate the block state forward to all successor blocks.
    for (int i = 0; i < block->end()->SuccessorCount(); i++) {
      HBasicBlock* succ = block->end()->SuccessorAt(i);
      if (!allocate_block->Dominates(succ)) continue;
      if (succ->predecessors()->length() == 1) {
        // Case 1: This is the only predecessor, just reuse state.
        SetStateAt(succ, state);
      } else if (StateAt(succ) == NULL && succ->IsLoopHeader()) {
        // Case 2: This is a state that enters a loop header, be
        // pessimistic about loop headers, add phis for all values.
        SetStateAt(succ, NewStateForLoopHeader(succ->first(), state));
      } else if (StateAt(succ) == NULL) {
        // Case 3: This is the first state propagated forward to the
        // successor, leave a copy of the current state.
        SetStateAt(succ, NewStateCopy(succ->first(), state));
      } else {
        // Case 4: This is a state that needs merging with previously
        // propagated states, potentially introducing new phis lazily or
        // adding values to existing phis.
        HCapturedObject* succ_state = StateAt(succ);
        for (int index = 0; index < number_of_values_; index++) {
          HValue* operand = state->OperandAt(index);
          HValue* succ_operand = succ_state->OperandAt(index);
          if (succ_operand->IsPhi() && succ_operand->block() == succ) {
            // Phi already exists, add operand.
            HPhi* phi = HPhi::cast(succ_operand);
            phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
          } else if (succ_operand != operand) {
            // Phi does not exist, introduce one.
            HPhi* phi = NewPhiAndInsert(succ, succ_operand, index);
            phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
            succ_state->SetOperandAt(index, phi);
          }
        }
      }
    }
  }

  // All uses have been handled.
289
  DCHECK(allocate->HasNoUses());
290 291 292 293 294 295 296 297 298 299 300
  allocate->DeleteAndReplaceWith(NULL);
}


void HEscapeAnalysisPhase::PerformScalarReplacement() {
  for (int i = 0; i < captured_.length(); i++) {
    HAllocate* allocate = HAllocate::cast(captured_.at(i));

    // Compute number of scalar values and start with clean slate.
    int size_in_bytes = allocate->size()->GetInteger32Constant();
    number_of_values_ = size_in_bytes / kPointerSize;
301
    number_of_objects_++;
302
    block_states_.Rewind(0);
303

304
    // Perform actual analysis step.
305 306 307
    AnalyzeDataFlow(allocate);

    cumulative_values_ += number_of_values_;
308 309
    DCHECK(allocate->HasNoUses());
    DCHECK(!allocate->IsLinked());
310 311 312 313
  }
}


314 315 316 317
void HEscapeAnalysisPhase::Run() {
  // TODO(mstarzinger): We disable escape analysis with OSR for now, because
  // spill slots might be uninitialized. Needs investigation.
  if (graph()->has_osr()) return;
318 319 320 321 322
  int max_fixpoint_iteration_count = FLAG_escape_analysis_iterations;
  for (int i = 0; i < max_fixpoint_iteration_count; i++) {
    CollectCapturedValues();
    if (captured_.is_empty()) break;
    PerformScalarReplacement();
323
    captured_.Rewind(0);
324
  }
325 326 327
}


328
} }  // namespace v8::internal