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-check-elimination.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 
30 #include "hydrogen-flow-engine.h"
31 
32 #define GLOBAL 1
33 
34 // Only collect stats in debug mode.
35 #if DEBUG
36 #define INC_STAT(x) phase_->x++
37 #else
38 #define INC_STAT(x)
39 #endif
40 
41 // For code de-uglification.
42 #define TRACE(x) if (FLAG_trace_check_elimination) PrintF x
43 
44 namespace v8 {
45 namespace internal {
46 
48 
50  HValue* object_; // The object being approximated. NULL => invalid entry.
51  HInstruction* check_; // The last check instruction.
52  MapSet maps_; // The set of known maps for the object.
53 };
54 
55 
56 // The main data structure used during check elimination, which stores a
57 // set of known maps for each object.
58 class HCheckTable : public ZoneObject {
59  public:
60  static const int kMaxTrackedObjects = 10;
61 
63  : phase_(phase),
64  cursor_(0),
65  size_(0) {
66  }
67 
68  // The main processing of instructions.
70  switch (instr->opcode()) {
71  case HValue::kCheckMaps: {
72  ReduceCheckMaps(HCheckMaps::cast(instr));
73  break;
74  }
75  case HValue::kCheckValue: {
76  ReduceCheckValue(HCheckValue::cast(instr));
77  break;
78  }
79  case HValue::kLoadNamedField: {
80  ReduceLoadNamedField(HLoadNamedField::cast(instr));
81  break;
82  }
83  case HValue::kStoreNamedField: {
84  ReduceStoreNamedField(HStoreNamedField::cast(instr));
85  break;
86  }
87  case HValue::kCompareMap: {
88  ReduceCompareMap(HCompareMap::cast(instr));
89  break;
90  }
91  case HValue::kCompareObjectEqAndBranch: {
92  ReduceCompareObjectEqAndBranch(HCompareObjectEqAndBranch::cast(instr));
93  break;
94  }
95  case HValue::kTransitionElementsKind: {
96  ReduceTransitionElementsKind(
97  HTransitionElementsKind::cast(instr));
98  break;
99  }
100  case HValue::kCheckMapValue: {
101  ReduceCheckMapValue(HCheckMapValue::cast(instr));
102  break;
103  }
104  case HValue::kCheckHeapObject: {
105  ReduceCheckHeapObject(HCheckHeapObject::cast(instr));
106  break;
107  }
108  default: {
109  // If the instruction changes maps uncontrollably, drop everything.
110  if (instr->CheckChangesFlag(kMaps) ||
111  instr->CheckChangesFlag(kOsrEntries)) {
112  Kill();
113  }
114  }
115  // Improvements possible:
116  // - eliminate redundant HCheckSmi, HCheckInstanceType instructions
117  // - track which values have been HCheckHeapObject'd
118  }
119 
120  return this;
121  }
122 
123  // Support for global analysis with HFlowEngine: Merge given state with
124  // the other incoming state.
125  static HCheckTable* Merge(HCheckTable* succ_state, HBasicBlock* succ_block,
126  HCheckTable* pred_state, HBasicBlock* pred_block,
127  Zone* zone) {
128  if (pred_state == NULL || pred_block->IsUnreachable()) {
129  return succ_state;
130  }
131  if (succ_state == NULL) {
132  return pred_state->Copy(succ_block, pred_block, zone);
133  } else {
134  return succ_state->Merge(succ_block, pred_state, pred_block, zone);
135  }
136  }
137 
138  // Support for global analysis with HFlowEngine: Given state merged with all
139  // the other incoming states, prepare it for use.
140  static HCheckTable* Finish(HCheckTable* state, HBasicBlock* block,
141  Zone* zone) {
142  if (state == NULL) {
143  block->MarkUnreachable();
144  } else if (block->IsUnreachable()) {
145  state = NULL;
146  }
147  if (FLAG_trace_check_elimination) {
148  PrintF("Processing B%d, checkmaps-table:\n", block->block_id());
149  Print(state);
150  }
151  return state;
152  }
153 
154  private:
155  // Copy state to successor block.
156  HCheckTable* Copy(HBasicBlock* succ, HBasicBlock* from_block, Zone* zone) {
157  HCheckTable* copy = new(phase_->zone()) HCheckTable(phase_);
158  for (int i = 0; i < size_; i++) {
159  HCheckTableEntry* old_entry = &entries_[i];
160  ASSERT(old_entry->maps_->size() > 0);
161  HCheckTableEntry* new_entry = &copy->entries_[i];
162  new_entry->object_ = old_entry->object_;
163  new_entry->maps_ = old_entry->maps_->Copy(phase_->zone());
164  // Keep the check if the existing check's block dominates the successor.
165  if (old_entry->check_ != NULL &&
166  old_entry->check_->block()->Dominates(succ)) {
167  new_entry->check_ = old_entry->check_;
168  } else {
169  // Leave it NULL till we meet a new check instruction for this object
170  // in the control flow.
171  new_entry->check_ = NULL;
172  }
173  }
174  copy->cursor_ = cursor_;
175  copy->size_ = size_;
176 
177  // Create entries for succ block's phis.
178  if (!succ->IsLoopHeader() && succ->phis()->length() > 0) {
179  int pred_index = succ->PredecessorIndexOf(from_block);
180  for (int phi_index = 0;
181  phi_index < succ->phis()->length();
182  ++phi_index) {
183  HPhi* phi = succ->phis()->at(phi_index);
184  HValue* phi_operand = phi->OperandAt(pred_index);
185 
186  HCheckTableEntry* pred_entry = copy->Find(phi_operand);
187  if (pred_entry != NULL) {
188  // Create an entry for a phi in the table.
189  copy->Insert(phi, NULL, pred_entry->maps_->Copy(phase_->zone()));
190  }
191  }
192  }
193 
194  // Branch-sensitive analysis for certain comparisons may add more facts
195  // to the state for the successor on the true branch.
196  bool learned = false;
197  if (succ->predecessors()->length() == 1) {
198  HControlInstruction* end = succ->predecessors()->at(0)->end();
199  bool is_true_branch = end->SuccessorAt(0) == succ;
200  if (end->IsCompareMap()) {
201  HCompareMap* cmp = HCompareMap::cast(end);
202  HValue* object = cmp->value()->ActualValue();
203  HCheckTableEntry* entry = copy->Find(object);
204  if (is_true_branch) {
205  // Learn on the true branch of if(CompareMap(x)).
206  if (entry == NULL) {
207  copy->Insert(object, cmp, cmp->map());
208  } else {
209  MapSet list = new(phase_->zone()) UniqueSet<Map>();
210  list->Add(cmp->map(), phase_->zone());
211  entry->maps_ = list;
212  entry->check_ = cmp;
213  }
214  } else {
215  // Learn on the false branch of if(CompareMap(x)).
216  if (entry != NULL) {
217  entry->maps_->Remove(cmp->map());
218  }
219  }
220  learned = true;
221  } else if (is_true_branch && end->IsCompareObjectEqAndBranch()) {
222  // Learn on the true branch of if(CmpObjectEq(x, y)).
223  HCompareObjectEqAndBranch* cmp =
225  HValue* left = cmp->left()->ActualValue();
226  HValue* right = cmp->right()->ActualValue();
227  HCheckTableEntry* le = copy->Find(left);
228  HCheckTableEntry* re = copy->Find(right);
229  if (le == NULL) {
230  if (re != NULL) {
231  copy->Insert(left, NULL, re->maps_->Copy(zone));
232  }
233  } else if (re == NULL) {
234  copy->Insert(right, NULL, le->maps_->Copy(zone));
235  } else {
236  MapSet intersect = le->maps_->Intersect(re->maps_, zone);
237  le->maps_ = intersect;
238  re->maps_ = intersect->Copy(zone);
239  }
240  learned = true;
241  }
242  // Learning on false branches requires storing negative facts.
243  }
244 
245  if (FLAG_trace_check_elimination) {
246  PrintF("B%d checkmaps-table %s from B%d:\n",
247  succ->block_id(),
248  learned ? "learned" : "copied",
249  from_block->block_id());
250  Print(copy);
251  }
252 
253  return copy;
254  }
255 
256  // Merge this state with the other incoming state.
257  HCheckTable* Merge(HBasicBlock* succ, HCheckTable* that,
258  HBasicBlock* pred_block, Zone* zone) {
259  if (that->size_ == 0) {
260  // If the other state is empty, simply reset.
261  size_ = 0;
262  cursor_ = 0;
263  } else {
264  int pred_index = succ->PredecessorIndexOf(pred_block);
265  bool compact = false;
266  for (int i = 0; i < size_; i++) {
267  HCheckTableEntry* this_entry = &entries_[i];
268  HCheckTableEntry* that_entry;
269  if (this_entry->object_->IsPhi() &&
270  this_entry->object_->block() == succ) {
271  HPhi* phi = HPhi::cast(this_entry->object_);
272  HValue* phi_operand = phi->OperandAt(pred_index);
273  that_entry = that->Find(phi_operand);
274 
275  } else {
276  that_entry = that->Find(this_entry->object_);
277  }
278 
279  if (that_entry == NULL) {
280  this_entry->object_ = NULL;
281  compact = true;
282  } else {
283  this_entry->maps_ =
284  this_entry->maps_->Union(that_entry->maps_, phase_->zone());
285  if (this_entry->check_ != that_entry->check_) {
286  this_entry->check_ = NULL;
287  }
288  ASSERT(this_entry->maps_->size() > 0);
289  }
290  }
291  if (compact) Compact();
292  }
293 
294  if (FLAG_trace_check_elimination) {
295  PrintF("B%d checkmaps-table merged with B%d table:\n",
296  succ->block_id(), pred_block->block_id());
297  Print(this);
298  }
299  return this;
300  }
301 
302  void ReduceCheckMaps(HCheckMaps* instr) {
303  HValue* object = instr->value()->ActualValue();
304  HCheckTableEntry* entry = Find(object);
305  if (entry != NULL) {
306  // entry found;
307  MapSet a = entry->maps_;
308  MapSet i = instr->map_set().Copy(phase_->zone());
309  if (a->IsSubset(i)) {
310  // The first check is more strict; the second is redundant.
311  if (entry->check_ != NULL) {
312  TRACE(("Replacing redundant CheckMaps #%d at B%d with #%d\n",
313  instr->id(), instr->block()->block_id(), entry->check_->id()));
314  instr->DeleteAndReplaceWith(entry->check_);
315  INC_STAT(redundant_);
316  } else {
317  TRACE(("Marking redundant CheckMaps #%d at B%d as dead\n",
318  instr->id(), instr->block()->block_id()));
319  // Mark check as dead but leave it in the graph as a checkpoint for
320  // subsequent checks.
321  instr->SetFlag(HValue::kIsDead);
322  entry->check_ = instr;
323  INC_STAT(removed_);
324  }
325  return;
326  }
327  MapSet intersection = i->Intersect(a, phase_->zone());
328  if (intersection->size() == 0) {
329  // Intersection is empty; probably megamorphic, which is likely to
330  // deopt anyway, so just leave things as they are.
331  INC_STAT(empty_);
332  } else {
333  // Update set of maps in the entry.
334  entry->maps_ = intersection;
335  if (intersection->size() != i->size()) {
336  // Narrow set of maps in the second check maps instruction.
337  HGraph* graph = instr->block()->graph();
338  if (entry->check_ != NULL &&
339  entry->check_->block() == instr->block() &&
340  entry->check_->IsCheckMaps()) {
341  // There is a check in the same block so replace it with a more
342  // strict check and eliminate the second check entirely.
343  HCheckMaps* check = HCheckMaps::cast(entry->check_);
344  TRACE(("CheckMaps #%d at B%d narrowed\n", check->id(),
345  check->block()->block_id()));
346  // Update map set and ensure that the check is alive.
347  check->set_map_set(intersection, graph->zone());
348  check->ClearFlag(HValue::kIsDead);
349  TRACE(("Replacing redundant CheckMaps #%d at B%d with #%d\n",
350  instr->id(), instr->block()->block_id(), entry->check_->id()));
351  instr->DeleteAndReplaceWith(entry->check_);
352  } else {
353  TRACE(("CheckMaps #%d at B%d narrowed\n", instr->id(),
354  instr->block()->block_id()));
355  instr->set_map_set(intersection, graph->zone());
356  entry->check_ = instr;
357  }
358 
359  if (FLAG_trace_check_elimination) {
360  Print(this);
361  }
362  INC_STAT(narrowed_);
363  }
364  }
365  } else {
366  // No entry; insert a new one.
367  Insert(object, instr, instr->map_set().Copy(phase_->zone()));
368  }
369  }
370 
371  void ReduceCheckValue(HCheckValue* instr) {
372  // Canonicalize HCheckValues; they might have their values load-eliminated.
373  HValue* value = instr->Canonicalize();
374  if (value == NULL) {
375  instr->DeleteAndReplaceWith(instr->value());
376  INC_STAT(removed_);
377  } else if (value != instr) {
378  instr->DeleteAndReplaceWith(value);
379  INC_STAT(redundant_);
380  }
381  }
382 
383  void ReduceLoadNamedField(HLoadNamedField* instr) {
384  // Reduce a load of the map field when it is known to be a constant.
385  if (!IsMapAccess(instr->access())) return;
386 
387  HValue* object = instr->object()->ActualValue();
388  MapSet maps = FindMaps(object);
389  if (maps == NULL || maps->size() != 1) return; // Not a constant.
390 
391  Unique<Map> map = maps->at(0);
392  HConstant* constant = HConstant::CreateAndInsertBefore(
393  instr->block()->graph()->zone(), map, true, instr);
394  instr->DeleteAndReplaceWith(constant);
395  INC_STAT(loads_);
396  }
397 
398  void ReduceCheckMapValue(HCheckMapValue* instr) {
399  if (!instr->map()->IsConstant()) return; // Nothing to learn.
400 
401  HValue* object = instr->value()->ActualValue();
402  // Match a HCheckMapValue(object, HConstant(map))
403  Unique<Map> map = MapConstant(instr->map());
404 
405  HCheckTableEntry* entry = Find(object);
406  if (entry != NULL) {
407  MapSet maps = entry->maps_;
408  if (maps->Contains(map)) {
409  if (maps->size() == 1) {
410  // Object is known to have exactly this map.
411  if (entry->check_ != NULL) {
412  instr->DeleteAndReplaceWith(entry->check_);
413  } else {
414  // Mark check as dead but leave it in the graph as a checkpoint for
415  // subsequent checks.
416  instr->SetFlag(HValue::kIsDead);
417  entry->check_ = instr;
418  }
419  INC_STAT(removed_);
420  } else {
421  // Only one map survives the check.
422  maps->Clear();
423  maps->Add(map, phase_->zone());
424  entry->check_ = instr;
425  }
426  }
427  } else {
428  // No prior information.
429  Insert(object, instr, map);
430  }
431  }
432 
433  void ReduceCheckHeapObject(HCheckHeapObject* instr) {
434  if (FindMaps(instr->value()->ActualValue()) != NULL) {
435  // If the object has known maps, it's definitely a heap object.
436  instr->DeleteAndReplaceWith(instr->value());
437  INC_STAT(removed_cho_);
438  }
439  }
440 
441  void ReduceStoreNamedField(HStoreNamedField* instr) {
442  HValue* object = instr->object()->ActualValue();
443  if (instr->has_transition()) {
444  // This store transitions the object to a new map.
445  Kill(object);
446  Insert(object, NULL, MapConstant(instr->transition()));
447  } else if (IsMapAccess(instr->access())) {
448  // This is a store directly to the map field of the object.
449  Kill(object);
450  if (!instr->value()->IsConstant()) return;
451  Insert(object, NULL, MapConstant(instr->value()));
452  } else {
453  // If the instruction changes maps, it should be handled above.
454  CHECK(!instr->CheckChangesFlag(kMaps));
455  }
456  }
457 
458  void ReduceCompareMap(HCompareMap* instr) {
459  MapSet maps = FindMaps(instr->value()->ActualValue());
460  if (maps == NULL) return;
461 
462  int succ;
463  if (maps->Contains(instr->map())) {
464  if (maps->size() != 1) {
465  TRACE(("CompareMap #%d for #%d at B%d can't be eliminated: "
466  "ambiguous set of maps\n", instr->id(), instr->value()->id(),
467  instr->block()->block_id()));
468  return;
469  }
470  succ = 0;
471  INC_STAT(compares_true_);
472  } else {
473  succ = 1;
474  INC_STAT(compares_false_);
475  }
476 
477  TRACE(("Marking redundant CompareMap #%d for #%d at B%d as %s\n",
478  instr->id(), instr->value()->id(), instr->block()->block_id(),
479  succ == 0 ? "true" : "false"));
480  instr->set_known_successor_index(succ);
481 
482  int unreachable_succ = 1 - succ;
483  instr->block()->MarkSuccEdgeUnreachable(unreachable_succ);
484  }
485 
486  void ReduceCompareObjectEqAndBranch(HCompareObjectEqAndBranch* instr) {
487  MapSet maps_left = FindMaps(instr->left()->ActualValue());
488  if (maps_left == NULL) return;
489  MapSet maps_right = FindMaps(instr->right()->ActualValue());
490  if (maps_right == NULL) return;
491  MapSet intersection = maps_left->Intersect(maps_right, phase_->zone());
492  if (intersection->size() > 0) return;
493 
494  TRACE(("Marking redundant CompareObjectEqAndBranch #%d at B%d as false\n",
495  instr->id(), instr->block()->block_id()));
496  int succ = 1;
497  instr->set_known_successor_index(succ);
498 
499  int unreachable_succ = 1 - succ;
500  instr->block()->MarkSuccEdgeUnreachable(unreachable_succ);
501  }
502 
503  void ReduceTransitionElementsKind(HTransitionElementsKind* instr) {
504  MapSet maps = FindMaps(instr->object()->ActualValue());
505  // Can only learn more about an object that already has a known set of maps.
506  if (maps == NULL) return;
507  if (maps->Contains(instr->original_map())) {
508  // If the object has the original map, it will be transitioned.
509  maps->Remove(instr->original_map());
510  maps->Add(instr->transitioned_map(), phase_->zone());
511  } else {
512  // Object does not have the given map, thus the transition is redundant.
513  instr->DeleteAndReplaceWith(instr->object());
514  INC_STAT(transitions_);
515  }
516  }
517 
518  // Kill everything in the table.
519  void Kill() {
520  size_ = 0;
521  cursor_ = 0;
522  }
523 
524  // Kill everything in the table that may alias {object}.
525  void Kill(HValue* object) {
526  bool compact = false;
527  for (int i = 0; i < size_; i++) {
528  HCheckTableEntry* entry = &entries_[i];
529  ASSERT(entry->object_ != NULL);
530  if (phase_->aliasing_->MayAlias(entry->object_, object)) {
531  entry->object_ = NULL;
532  compact = true;
533  }
534  }
535  if (compact) Compact();
536  ASSERT(Find(object) == NULL);
537  }
538 
539  void Compact() {
540  // First, compact the array in place.
541  int max = size_, dest = 0, old_cursor = cursor_;
542  for (int i = 0; i < max; i++) {
543  if (entries_[i].object_ != NULL) {
544  if (dest != i) entries_[dest] = entries_[i];
545  dest++;
546  } else {
547  if (i < old_cursor) cursor_--;
548  size_--;
549  }
550  }
551  ASSERT(size_ == dest);
552  ASSERT(cursor_ <= size_);
553 
554  // Preserve the age of the entries by moving the older entries to the end.
555  if (cursor_ == size_) return; // Cursor already points at end.
556  if (cursor_ != 0) {
557  // | L = oldest | R = newest | |
558  // ^ cursor ^ size ^ MAX
559  HCheckTableEntry tmp_entries[kMaxTrackedObjects];
560  int L = cursor_;
561  int R = size_ - cursor_;
562 
563  OS::MemMove(&tmp_entries[0], &entries_[0], L * sizeof(HCheckTableEntry));
564  OS::MemMove(&entries_[0], &entries_[L], R * sizeof(HCheckTableEntry));
565  OS::MemMove(&entries_[R], &tmp_entries[0], L * sizeof(HCheckTableEntry));
566  }
567 
568  cursor_ = size_; // Move cursor to end.
569  }
570 
571  static void Print(HCheckTable* table) {
572  if (table == NULL) {
573  PrintF(" unreachable\n");
574  return;
575  }
576 
577  for (int i = 0; i < table->size_; i++) {
578  HCheckTableEntry* entry = &table->entries_[i];
579  ASSERT(entry->object_ != NULL);
580  PrintF(" checkmaps-table @%d: %s #%d ", i,
581  entry->object_->IsPhi() ? "phi" : "object", entry->object_->id());
582  if (entry->check_ != NULL) {
583  PrintF("check #%d ", entry->check_->id());
584  }
585  MapSet list = entry->maps_;
586  PrintF("%d maps { ", list->size());
587  for (int j = 0; j < list->size(); j++) {
588  if (j > 0) PrintF(", ");
589  PrintF("%" V8PRIxPTR, list->at(j).Hashcode());
590  }
591  PrintF(" }\n");
592  }
593  }
594 
595  HCheckTableEntry* Find(HValue* object) {
596  for (int i = size_ - 1; i >= 0; i--) {
597  // Search from most-recently-inserted to least-recently-inserted.
598  HCheckTableEntry* entry = &entries_[i];
599  ASSERT(entry->object_ != NULL);
600  if (phase_->aliasing_->MustAlias(entry->object_, object)) return entry;
601  }
602  return NULL;
603  }
604 
605  MapSet FindMaps(HValue* object) {
606  HCheckTableEntry* entry = Find(object);
607  return entry == NULL ? NULL : entry->maps_;
608  }
609 
610  void Insert(HValue* object, HInstruction* check, Unique<Map> map) {
611  MapSet list = new(phase_->zone()) UniqueSet<Map>();
612  list->Add(map, phase_->zone());
613  Insert(object, check, list);
614  }
615 
616  void Insert(HValue* object, HInstruction* check, MapSet maps) {
617  HCheckTableEntry* entry = &entries_[cursor_++];
618  entry->object_ = object;
619  entry->check_ = check;
620  entry->maps_ = maps;
621  // If the table becomes full, wrap around and overwrite older entries.
622  if (cursor_ == kMaxTrackedObjects) cursor_ = 0;
623  if (size_ < kMaxTrackedObjects) size_++;
624  }
625 
626  bool IsMapAccess(HObjectAccess access) {
627  return access.IsInobject() && access.offset() == JSObject::kMapOffset;
628  }
629 
630  Unique<Map> MapConstant(HValue* value) {
631  return Unique<Map>::cast(HConstant::cast(value)->GetUnique());
632  }
633 
634  friend class HCheckMapsEffects;
636 
637  HCheckEliminationPhase* phase_;
639  int16_t cursor_; // Must be <= kMaxTrackedObjects
640  int16_t size_; // Must be <= kMaxTrackedObjects
641  // TODO(titzer): STATIC_ASSERT kMaxTrackedObjects < max(cursor_)
642 };
643 
644 
645 // Collects instructions that can cause effects that invalidate information
646 // needed for check elimination.
648  public:
649  explicit HCheckMapsEffects(Zone* zone)
650  : maps_stored_(false),
651  stores_(5, zone) { }
652 
653  inline bool Disabled() {
654  return false; // Effects are _not_ disabled.
655  }
656 
657  // Process a possibly side-effecting instruction.
658  void Process(HInstruction* instr, Zone* zone) {
659  switch (instr->opcode()) {
660  case HValue::kStoreNamedField: {
661  stores_.Add(HStoreNamedField::cast(instr), zone);
662  break;
663  }
664  case HValue::kOsrEntry: {
665  // Kill everything. Loads must not be hoisted past the OSR entry.
666  maps_stored_ = true;
667  }
668  default: {
669  maps_stored_ |= (instr->CheckChangesFlag(kMaps) |
670  instr->CheckChangesFlag(kElementsKind));
671  }
672  }
673  }
674 
675  // Apply these effects to the given check elimination table.
676  void Apply(HCheckTable* table) {
677  if (maps_stored_) {
678  // Uncontrollable map modifications; kill everything.
679  table->Kill();
680  return;
681  }
682 
683  // Kill maps for each store contained in these effects.
684  for (int i = 0; i < stores_.length(); i++) {
685  HStoreNamedField* s = stores_[i];
686  if (table->IsMapAccess(s->access()) || s->has_transition()) {
687  table->Kill(s->object()->ActualValue());
688  }
689  }
690  }
691 
692  // Union these effects with the other effects.
693  void Union(HCheckMapsEffects* that, Zone* zone) {
694  maps_stored_ |= that->maps_stored_;
695  for (int i = 0; i < that->stores_.length(); i++) {
696  stores_.Add(that->stores_[i], zone);
697  }
698  }
699 
700  private:
701  bool maps_stored_ : 1;
703 };
704 
705 
706 // The main routine of the analysis phase. Use the HFlowEngine for either a
707 // local or a global analysis.
710  HCheckTable* table = new(zone()) HCheckTable(this);
711 
712  if (GLOBAL) {
713  // Perform a global analysis.
714  engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), table);
715  } else {
716  // Perform only local analysis.
717  for (int i = 0; i < graph()->blocks()->length(); i++) {
718  table->Kill();
719  engine.AnalyzeOneBlock(graph()->blocks()->at(i), table);
720  }
721  }
722 
723  if (FLAG_trace_check_elimination) PrintStats();
724 }
725 
726 
727 // Are we eliminated yet?
728 void HCheckEliminationPhase::PrintStats() {
729 #if DEBUG
730  #define PRINT_STAT(x) if (x##_ > 0) PrintF(" %-16s = %2d\n", #x, x##_)
731 #else
732  #define PRINT_STAT(x)
733 #endif
734  PRINT_STAT(redundant);
735  PRINT_STAT(removed);
736  PRINT_STAT(removed_cho);
737  PRINT_STAT(narrowed);
738  PRINT_STAT(loads);
739  PRINT_STAT(empty);
740  PRINT_STAT(compares_true);
741  PRINT_STAT(compares_false);
742  PRINT_STAT(transitions);
743 }
744 
745 } } // 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
#define V8PRIxPTR
Definition: globals.h:228
void PrintF(const char *format,...)
Definition: v8utils.cc:40
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 trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf map
Definition: flags.cc:350
bool CheckChangesFlag(GVNFlag f) const
HBasicBlock * block() const
void AnalyzeDominatedBlocks(HBasicBlock *root, State *initial)
HValue * object_
#define ASSERT(condition)
Definition: checks.h:329
HGraph * graph() const
Definition: hydrogen.h:2695
void Process(HInstruction *instr, Zone *zone)
#define CHECK(condition)
Definition: checks.h:75
#define INC_STAT(x)
UniqueSet< Map > * MapSet
bool MayAlias(HValue *a, HValue *b)
MapSet maps_
#define TRACE(x)
#define GLOBAL
void check(i::Vector< const uint8_t > string)
#define PRINT_STAT(x)
static void MemMove(void *dest, const void *src, size_t size)
Definition: platform.h:402
HInstruction * check_
HCheckTable * Process(HInstruction *instr, Zone *zone)
bool MustAlias(HValue *a, HValue *b)
void Union(HCheckMapsEffects *that, Zone *zone)
static HCheckTable * Merge(HCheckTable *succ_state, HBasicBlock *succ_block, HCheckTable *pred_state, HBasicBlock *pred_block, Zone *zone)
virtual Opcode opcode() const =0
static const int kMapOffset
Definition: objects.h:1890
State * AnalyzeOneBlock(HBasicBlock *block, State *state)
void Add(const T &element, AllocationPolicy allocator=AllocationPolicy())
Definition: list-inl.h:39
signed short int16_t
Definition: unicode.cc:45
HCheckTable(HCheckEliminationPhase *phase)
static HValue * cast(HValue *value)
static HCheckTable * Finish(HCheckTable *state, HBasicBlock *block, Zone *zone)