Chromium Code Reviews| Index: src/hydrogen-check-elimination.cc |
| diff --git a/src/hydrogen-check-elimination.cc b/src/hydrogen-check-elimination.cc |
| index ae11042ba1e449b3c33b2d036ca36477165142d6..627a1adc61fe6c1a6dddc4fa7399aaadbe64fdca 100644 |
| --- a/src/hydrogen-check-elimination.cc |
| +++ b/src/hydrogen-check-elimination.cc |
| @@ -117,7 +117,7 @@ class HCheckTable : public ZoneObject { |
| } |
| // Global analysis: Copy state to successor block. |
| - HCheckTable* Copy(HBasicBlock* succ, Zone* zone) { |
| + HCheckTable* Copy(HBasicBlock* succ, HBasicBlock* from_block, Zone* zone) { |
| HCheckTable* copy = new(phase_->zone()) HCheckTable(phase_); |
| for (int i = 0; i < size_; i++) { |
| HCheckTableEntry* old_entry = &entries_[i]; |
| @@ -130,8 +130,11 @@ class HCheckTable : public ZoneObject { |
| copy->cursor_ = cursor_; |
| copy->size_ = size_; |
| + copy->InheritPhiOperands(succ, this, from_block, COPY, zone); |
| + |
| // Branch-sensitive analysis for certain comparisons may add more facts |
| // to the state for the successor on the true branch. |
| + bool learned = false; |
| HControlInstruction* end = succ->predecessors()->at(0)->end(); |
| if (succ->predecessors()->length() == 1 && end->SuccessorAt(0) == succ) { |
| if (end->IsCompareMap()) { |
| @@ -146,6 +149,7 @@ class HCheckTable : public ZoneObject { |
| list->Add(cmp->map(), phase_->zone()); |
| entry->maps_ = list; |
| } |
| + learned = true; |
| } else if (end->IsCompareObjectEqAndBranch()) { |
| // Learn on the true branch of if(CmpObjectEq(x, y)). |
| HCompareObjectEqAndBranch* cmp = |
| @@ -165,37 +169,99 @@ class HCheckTable : public ZoneObject { |
| le->maps_ = intersect; |
| re->maps_ = intersect->Copy(zone); |
| } |
| + learned = true; |
| } |
| // Learning on false branches requires storing negative facts. |
| } |
| + if (FLAG_trace_check_elimination) { |
| + PrintF("B%d checkmaps-table %s from B%d:\n", |
| + succ->block_id(), |
| + learned ? "learned" : "copied", |
| + from_block->block_id()); |
| + copy->Print(); |
| + } |
| + |
| return copy; |
| } |
| // Global analysis: Merge this state with the other incoming state. |
| - HCheckTable* Merge(HBasicBlock* succ, HCheckTable* that, Zone* zone) { |
| - if (that->size_ == 0) { |
| - // If the other state is empty, simply reset. |
| - size_ = 0; |
| - cursor_ = 0; |
| - return this; |
| - } |
| - bool compact = false; |
| - for (int i = 0; i < size_; i++) { |
| - HCheckTableEntry* this_entry = &entries_[i]; |
| - HCheckTableEntry* that_entry = that->Find(this_entry->object_); |
| - if (that_entry == NULL) { |
| - this_entry->object_ = NULL; |
| - compact = true; |
| + void Merge(HBasicBlock* succ, HCheckTable* that, |
| + HBasicBlock* that_block, Zone* zone) { |
| + if (that_block->IsReachable()) { |
| + if (that->size_ == 0) { |
| + // If the other state is empty, simply reset. |
| + size_ = 0; |
| + cursor_ = 0; |
| } else { |
| - this_entry->maps_ = this_entry->maps_->Union( |
| - that_entry->maps_, phase_->zone()); |
| - if (this_entry->check_ != that_entry->check_) this_entry->check_ = NULL; |
| - ASSERT(this_entry->maps_->size() > 0); |
| + InheritPhiOperands(succ, that, that_block, MERGE, zone); |
| + |
| + bool compact = false; |
| + for (int i = 0; i < size_; i++) { |
| + HCheckTableEntry* this_entry = &entries_[i]; |
| + HCheckTableEntry* that_entry = that->Find(this_entry->object_); |
| + // Keep those phis in the table that dominate succ block. |
| + if (that_entry == NULL) { |
| + if (!this_entry->object_->IsPhi() || |
| + !this_entry->object_->block()->EqualToOrDominates(succ)) { |
| + this_entry->object_ = NULL; |
| + compact = true; |
| + } |
| + } else { |
| + this_entry->maps_ = |
| + this_entry->maps_->Union(that_entry->maps_, phase_->zone()); |
| + if (this_entry->check_ != that_entry->check_) { |
| + this_entry->check_ = NULL; |
| + } |
| + ASSERT(this_entry->maps_->size() > 0); |
| + } |
| + } |
| + if (compact) Compact(); |
| + } |
| + } |
| + |
| + if (FLAG_trace_check_elimination) { |
| + PrintF("B%d checkmaps-table merged with B%d table:\n", |
| + succ->block_id(), that_block->block_id()); |
| + Print(); |
| + } |
| + } |
| + |
| + enum InheritPhiOperandsMode { COPY, MERGE }; |
| + void InheritPhiOperands(HBasicBlock* succ, HCheckTable* that, |
| + HBasicBlock* that_block, InheritPhiOperandsMode mode, |
| + Zone* zone) { |
| + if (succ->predecessors()->length() == 1) return; |
| + |
| + int pred_index = succ->PredecessorIndexOf(that_block); |
| + |
| + for (int i = 0; i < that->size_; i++) { |
| + HCheckTableEntry* that_entry = &that->entries_[i]; |
| + if (that_entry->object_ != NULL) { |
| + HPhi* phi = NULL; |
| + for (int phi_index = 0; |
| + phi_index < succ->phis()->length(); |
| + ++phi_index) { |
| + HPhi* tmp_phi = succ->phis()->at(phi_index); |
| + if (tmp_phi->OperandAt(pred_index) == that_entry->object_) { |
| + phi = tmp_phi; |
| + break; |
| + } |
| + } |
| + |
| + if (phi != NULL) { |
| + // Found a |phi| with operand equal to |that_entry->object_|. |
| + HCheckTableEntry* phi_entry = (mode == MERGE) ? Find(phi) : NULL; |
| + if (phi_entry == NULL) { |
| + // Create an entry for a phi in the table. |
| + Insert(phi, NULL, that_entry->maps_); |
| + } else { |
| + phi_entry->maps_ = |
| + phi_entry->maps_->Union(that_entry->maps_, phase_->zone()); |
| + } |
| + } |
| } |
| } |
| - if (compact) Compact(); |
| - return this; |
| } |
| void ReduceCheckMaps(HCheckMaps* instr) { |
| @@ -208,21 +274,49 @@ class HCheckTable : public ZoneObject { |
| if (a->IsSubset(i)) { |
| // The first check is more strict; the second is redundant. |
| if (entry->check_ != NULL) { |
| + TRACE(("Replacing redundant CheckMaps #%d at B%d with #%d\n", |
| + instr->id(), instr->block()->block_id(), entry->check_->id())); |
| instr->DeleteAndReplaceWith(entry->check_); |
| INC_STAT(redundant_); |
| } else { |
| - instr->DeleteAndReplaceWith(instr->value()); |
| + TRACE(("Marking redundant CheckMaps #%d at B%d as dead\n", |
| + instr->id(), instr->block()->block_id())); |
| + // Mark check as dead but leave it in the graph as a checkpoint for |
| + // subsequent checks. |
| + instr->SetFlag(HValue::kIsDead); |
| + entry->check_ = instr; |
| INC_STAT(removed_); |
| } |
| return; |
| } |
| - i = i->Intersect(a, phase_->zone()); |
| - if (i->size() == 0) { |
| + MapSet narrowed_maps = i->Intersect(a, phase_->zone()); |
|
titzer
2014/01/23 18:40:55
Would prefer the name "intersection" instead of na
Igor Sheludko
2014/01/29 12:27:13
Done.
|
| + if (narrowed_maps->size() == 0) { |
| // Intersection is empty; probably megamorphic, which is likely to |
| // deopt anyway, so just leave things as they are. |
| INC_STAT(empty_); |
| } else { |
| - // TODO(titzer): replace the first check with a more strict check |
| + // Narrow set of maps in the second check and replace the first |
| + // check with a more strict one. |
| + entry->maps_ = narrowed_maps; |
| + if (narrowed_maps->size() != i->size()) { |
| + // Narrow set of maps in the second check maps instruction. |
| + HGraph* graph = instr->block()->graph(); |
| + HCheckMaps* new_check_maps = |
| + HCheckMaps::New(graph->zone(), NULL, instr->value(), |
| + narrowed_maps, instr->typecheck()); |
| + new_check_maps->InsertBefore(instr); |
| + TRACE(("CheckMaps #%d for #%d narrowed to #%d:\n", |
| + instr->id(), instr->value()->id(), new_check_maps->id())); |
| + instr->DeleteAndReplaceWith(new_check_maps); |
|
titzer
2014/01/23 18:40:55
I think you want to replace both entry->check_ (if
Igor Sheludko
2014/01/29 12:27:13
Done.
|
| + entry->check_ = new_check_maps; |
| + } else { |
| + TRACE(("CheckMaps #%d for #%d narrowed:\n", |
| + instr->id(), instr->value()->id())); |
|
titzer
2014/01/23 18:40:55
In this case, you haven't actually changed anythin
Igor Sheludko
2014/01/29 12:27:13
Done.
|
| + entry->check_ = instr; |
| + } |
| + if (FLAG_trace_check_elimination) { |
| + Print(); |
| + } |
| INC_STAT(narrowed_); |
| } |
| } else { |