v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hydrogen-escape-analysis.cc
Go to the documentation of this file.
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 
29 
30 namespace v8 {
31 namespace internal {
32 
33 
34 bool HEscapeAnalysisPhase::HasNoEscapingUses(HValue* value, int size) {
35  for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
36  HValue* use = it.value();
37  if (use->HasEscapingOperandAt(it.index())) {
38  if (FLAG_trace_escape_analysis) {
39  PrintF("#%d (%s) escapes through #%d (%s) @%d\n", value->id(),
40  value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
41  }
42  return false;
43  }
44  if (use->HasOutOfBoundsAccess(size)) {
45  if (FLAG_trace_escape_analysis) {
46  PrintF("#%d (%s) out of bounds at #%d (%s) @%d\n", value->id(),
47  value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
48  }
49  return false;
50  }
51  int redefined_index = use->RedefinedOperandIndex();
52  if (redefined_index == it.index() && !HasNoEscapingUses(use, size)) {
53  if (FLAG_trace_escape_analysis) {
54  PrintF("#%d (%s) escapes redefinition #%d (%s) @%d\n", value->id(),
55  value->Mnemonic(), use->id(), use->Mnemonic(), it.index());
56  }
57  return false;
58  }
59  }
60  return true;
61 }
62 
63 
64 void HEscapeAnalysisPhase::CollectCapturedValues() {
65  int block_count = graph()->blocks()->length();
66  for (int i = 0; i < block_count; ++i) {
67  HBasicBlock* block = graph()->blocks()->at(i);
68  for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
69  HInstruction* instr = it.Current();
70  if (!instr->IsAllocate()) continue;
71  HAllocate* allocate = HAllocate::cast(instr);
72  if (!allocate->size()->IsInteger32Constant()) continue;
73  int size_in_bytes = allocate->size()->GetInteger32Constant();
74  if (HasNoEscapingUses(instr, size_in_bytes)) {
75  if (FLAG_trace_escape_analysis) {
76  PrintF("#%d (%s) is being captured\n", instr->id(),
77  instr->Mnemonic());
78  }
79  captured_.Add(instr, zone());
80  }
81  }
82  }
83 }
84 
85 
86 HCapturedObject* HEscapeAnalysisPhase::NewState(HInstruction* previous) {
87  Zone* zone = graph()->zone();
88  HCapturedObject* state =
89  new(zone) HCapturedObject(number_of_values_, number_of_objects_, zone);
90  state->InsertAfter(previous);
91  return state;
92 }
93 
94 
95 // Create a new state for replacing HAllocate instructions.
96 HCapturedObject* HEscapeAnalysisPhase::NewStateForAllocation(
97  HInstruction* previous) {
98  HConstant* undefined = graph()->GetConstantUndefined();
99  HCapturedObject* state = NewState(previous);
100  for (int index = 0; index < number_of_values_; index++) {
101  state->SetOperandAt(index, undefined);
102  }
103  return state;
104 }
105 
106 
107 // Create a new state full of phis for loop header entries.
108 HCapturedObject* HEscapeAnalysisPhase::NewStateForLoopHeader(
109  HInstruction* previous,
110  HCapturedObject* old_state) {
111  HBasicBlock* block = previous->block();
112  HCapturedObject* state = NewState(previous);
113  for (int index = 0; index < number_of_values_; index++) {
114  HValue* operand = old_state->OperandAt(index);
115  HPhi* phi = NewPhiAndInsert(block, operand, index);
116  state->SetOperandAt(index, phi);
117  }
118  return state;
119 }
120 
121 
122 // Create a new state by copying an existing one.
123 HCapturedObject* HEscapeAnalysisPhase::NewStateCopy(
124  HInstruction* previous,
125  HCapturedObject* old_state) {
126  HCapturedObject* state = NewState(previous);
127  for (int index = 0; index < number_of_values_; index++) {
128  HValue* operand = old_state->OperandAt(index);
129  state->SetOperandAt(index, operand);
130  }
131  return state;
132 }
133 
134 
135 // Insert a newly created phi into the given block and fill all incoming
136 // edges with the given value.
137 HPhi* HEscapeAnalysisPhase::NewPhiAndInsert(HBasicBlock* block,
138  HValue* incoming_value,
139  int index) {
140  Zone* zone = graph()->zone();
141  HPhi* phi = new(zone) HPhi(HPhi::kInvalidMergedIndex, zone);
142  for (int i = 0; i < block->predecessors()->length(); i++) {
143  phi->AddInput(incoming_value);
144  }
145  block->AddPhi(phi);
146  return phi;
147 }
148 
149 
150 // Insert a newly created value check as a replacement for map checks.
151 HValue* HEscapeAnalysisPhase::NewMapCheckAndInsert(HCapturedObject* state,
152  HCheckMaps* mapcheck) {
153  Zone* zone = graph()->zone();
154  HValue* value = state->map_value();
155  // TODO(mstarzinger): This will narrow a map check against a set of maps
156  // down to the first element in the set. Revisit and fix this.
157  HCheckValue* check = HCheckValue::New(
158  zone, NULL, value, mapcheck->first_map(), false);
159  check->InsertBefore(mapcheck);
160  return check;
161 }
162 
163 
164 // Performs a forward data-flow analysis of all loads and stores on the
165 // given captured allocation. This uses a reverse post-order iteration
166 // over affected basic blocks. All non-escaping instructions are handled
167 // and replaced during the analysis.
168 void HEscapeAnalysisPhase::AnalyzeDataFlow(HInstruction* allocate) {
169  HBasicBlock* allocate_block = allocate->block();
170  block_states_.AddBlock(NULL, graph()->blocks()->length(), zone());
171 
172  // Iterate all blocks starting with the allocation block, since the
173  // allocation cannot dominate blocks that come before.
174  int start = allocate_block->block_id();
175  for (int i = start; i < graph()->blocks()->length(); i++) {
176  HBasicBlock* block = graph()->blocks()->at(i);
177  HCapturedObject* state = StateAt(block);
178 
179  // Skip blocks that are not dominated by the captured allocation.
180  if (!allocate_block->Dominates(block) && allocate_block != block) continue;
181  if (FLAG_trace_escape_analysis) {
182  PrintF("Analyzing data-flow in B%d\n", block->block_id());
183  }
184 
185  // Go through all instructions of the current block.
186  for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
187  HInstruction* instr = it.Current();
188  switch (instr->opcode()) {
189  case HValue::kAllocate: {
190  if (instr != allocate) continue;
191  state = NewStateForAllocation(allocate);
192  break;
193  }
194  case HValue::kLoadNamedField: {
195  HLoadNamedField* load = HLoadNamedField::cast(instr);
196  int index = load->access().offset() / kPointerSize;
197  if (load->object() != allocate) continue;
198  ASSERT(load->access().IsInobject());
199  HValue* replacement = state->OperandAt(index);
200  load->DeleteAndReplaceWith(replacement);
201  if (FLAG_trace_escape_analysis) {
202  PrintF("Replacing load #%d with #%d (%s)\n", instr->id(),
203  replacement->id(), replacement->Mnemonic());
204  }
205  break;
206  }
207  case HValue::kStoreNamedField: {
208  HStoreNamedField* store = HStoreNamedField::cast(instr);
209  int index = store->access().offset() / kPointerSize;
210  if (store->object() != allocate) continue;
211  ASSERT(store->access().IsInobject());
212  state = NewStateCopy(store->previous(), state);
213  state->SetOperandAt(index, store->value());
214  if (store->has_transition()) {
215  state->SetOperandAt(0, store->transition());
216  }
217  if (store->HasObservableSideEffects()) {
218  state->ReuseSideEffectsFromStore(store);
219  }
220  store->DeleteAndReplaceWith(store->ActualValue());
221  if (FLAG_trace_escape_analysis) {
222  PrintF("Replacing store #%d%s\n", instr->id(),
223  store->has_transition() ? " (with transition)" : "");
224  }
225  break;
226  }
227  case HValue::kArgumentsObject:
228  case HValue::kCapturedObject:
229  case HValue::kSimulate: {
230  for (int i = 0; i < instr->OperandCount(); i++) {
231  if (instr->OperandAt(i) != allocate) continue;
232  instr->SetOperandAt(i, state);
233  }
234  break;
235  }
236  case HValue::kCheckHeapObject: {
237  HCheckHeapObject* check = HCheckHeapObject::cast(instr);
238  if (check->value() != allocate) continue;
239  check->DeleteAndReplaceWith(check->ActualValue());
240  break;
241  }
242  case HValue::kCheckMaps: {
243  HCheckMaps* mapcheck = HCheckMaps::cast(instr);
244  if (mapcheck->value() != allocate) continue;
245  NewMapCheckAndInsert(state, mapcheck);
246  mapcheck->DeleteAndReplaceWith(mapcheck->ActualValue());
247  break;
248  }
249  default:
250  // Nothing to see here, move along ...
251  break;
252  }
253  }
254 
255  // Propagate the block state forward to all successor blocks.
256  for (int i = 0; i < block->end()->SuccessorCount(); i++) {
257  HBasicBlock* succ = block->end()->SuccessorAt(i);
258  if (!allocate_block->Dominates(succ)) continue;
259  if (succ->predecessors()->length() == 1) {
260  // Case 1: This is the only predecessor, just reuse state.
261  SetStateAt(succ, state);
262  } else if (StateAt(succ) == NULL && succ->IsLoopHeader()) {
263  // Case 2: This is a state that enters a loop header, be
264  // pessimistic about loop headers, add phis for all values.
265  SetStateAt(succ, NewStateForLoopHeader(succ->first(), state));
266  } else if (StateAt(succ) == NULL) {
267  // Case 3: This is the first state propagated forward to the
268  // successor, leave a copy of the current state.
269  SetStateAt(succ, NewStateCopy(succ->first(), state));
270  } else {
271  // Case 4: This is a state that needs merging with previously
272  // propagated states, potentially introducing new phis lazily or
273  // adding values to existing phis.
274  HCapturedObject* succ_state = StateAt(succ);
275  for (int index = 0; index < number_of_values_; index++) {
276  HValue* operand = state->OperandAt(index);
277  HValue* succ_operand = succ_state->OperandAt(index);
278  if (succ_operand->IsPhi() && succ_operand->block() == succ) {
279  // Phi already exists, add operand.
280  HPhi* phi = HPhi::cast(succ_operand);
281  phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
282  } else if (succ_operand != operand) {
283  // Phi does not exist, introduce one.
284  HPhi* phi = NewPhiAndInsert(succ, succ_operand, index);
285  phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
286  succ_state->SetOperandAt(index, phi);
287  }
288  }
289  }
290  }
291  }
292 
293  // All uses have been handled.
294  ASSERT(allocate->HasNoUses());
295  allocate->DeleteAndReplaceWith(NULL);
296 }
297 
298 
299 void HEscapeAnalysisPhase::PerformScalarReplacement() {
300  for (int i = 0; i < captured_.length(); i++) {
301  HAllocate* allocate = HAllocate::cast(captured_.at(i));
302 
303  // Compute number of scalar values and start with clean slate.
304  int size_in_bytes = allocate->size()->GetInteger32Constant();
305  number_of_values_ = size_in_bytes / kPointerSize;
306  number_of_objects_++;
307  block_states_.Clear();
308 
309  // Perform actual analysis step.
310  AnalyzeDataFlow(allocate);
311 
312  cumulative_values_ += number_of_values_;
313  ASSERT(allocate->HasNoUses());
314  ASSERT(!allocate->IsLinked());
315  }
316 }
317 
318 
320  // TODO(mstarzinger): We disable escape analysis with OSR for now, because
321  // spill slots might be uninitialized. Needs investigation.
322  if (graph()->has_osr()) return;
323  int max_fixpoint_iteration_count = FLAG_escape_analysis_iterations;
324  for (int i = 0; i < max_fixpoint_iteration_count; i++) {
325  CollectCapturedValues();
326  if (captured_.is_empty()) break;
327  PerformScalarReplacement();
328  captured_.Clear();
329  }
330 }
331 
332 
333 } } // namespace v8::internal
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
Definition: flags.cc:269
void PrintF(const char *format,...)
Definition: v8utils.cc:40
#define ASSERT(condition)
Definition: checks.h:329
HGraph * graph() const
Definition: hydrogen.h:2695
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object size
Definition: flags.cc:211
const int kPointerSize
Definition: globals.h:268
void check(i::Vector< const uint8_t > string)
Vector< T > AddBlock(T value, int count, AllocationPolicy allocator=AllocationPolicy())
Definition: list-inl.h:99