| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/crankshaft/hydrogen-removable-simulates.h" |
| |
| #include "src/crankshaft/hydrogen-flow-engine.h" |
| #include "src/crankshaft/hydrogen-instructions.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class State : public ZoneObject { |
| public: |
| explicit State(Zone* zone) |
| : zone_(zone), mergelist_(2, zone), first_(true), mode_(NORMAL) { } |
| |
| State* Process(HInstruction* instr, Zone* zone) { |
| if (FLAG_trace_removable_simulates) { |
| PrintF("[%s with state %p in B%d: #%d %s]\n", |
| mode_ == NORMAL ? "processing" : "collecting", |
| reinterpret_cast<void*>(this), instr->block()->block_id(), |
| instr->id(), instr->Mnemonic()); |
| } |
| // Forward-merge "trains" of simulates after an instruction with observable |
| // side effects to keep live ranges short. |
| if (mode_ == COLLECT_CONSECUTIVE_SIMULATES) { |
| if (instr->IsSimulate()) { |
| HSimulate* current_simulate = HSimulate::cast(instr); |
| if (current_simulate->is_candidate_for_removal() && |
| !current_simulate->ast_id().IsNone()) { |
| Remember(current_simulate); |
| return this; |
| } |
| } |
| FlushSimulates(); |
| mode_ = NORMAL; |
| } |
| // Ensure there's a non-foldable HSimulate before an HEnterInlined to avoid |
| // folding across HEnterInlined. |
| DCHECK(!(instr->IsEnterInlined() && |
| HSimulate::cast(instr->previous())->is_candidate_for_removal())); |
| if (instr->IsLeaveInlined() || instr->IsReturn()) { |
| // Never fold simulates from inlined environments into simulates in the |
| // outer environment. Simply remove all accumulated simulates without |
| // merging. This is safe because simulates after instructions with side |
| // effects are never added to the merge list. The same reasoning holds for |
| // return instructions. |
| RemoveSimulates(); |
| return this; |
| } |
| if (instr->IsControlInstruction()) { |
| // Merge the accumulated simulates at the end of the block. |
| FlushSimulates(); |
| return this; |
| } |
| if (instr->IsCapturedObject()) { |
| // Do not merge simulates across captured objects - captured objects |
| // change environments during environment replay, and such changes |
| // would not be reflected in the simulate. |
| FlushSimulates(); |
| return this; |
| } |
| // Skip the non-simulates and the first simulate. |
| if (!instr->IsSimulate()) return this; |
| if (first_) { |
| first_ = false; |
| return this; |
| } |
| HSimulate* current_simulate = HSimulate::cast(instr); |
| if (!current_simulate->is_candidate_for_removal()) { |
| Remember(current_simulate); |
| FlushSimulates(); |
| } else if (current_simulate->ast_id().IsNone()) { |
| DCHECK(current_simulate->next()->IsEnterInlined()); |
| FlushSimulates(); |
| } else if (current_simulate->previous()->HasObservableSideEffects()) { |
| Remember(current_simulate); |
| mode_ = COLLECT_CONSECUTIVE_SIMULATES; |
| } else { |
| Remember(current_simulate); |
| } |
| |
| return this; |
| } |
| |
| static State* Merge(State* succ_state, |
| HBasicBlock* succ_block, |
| State* pred_state, |
| HBasicBlock* pred_block, |
| Zone* zone) { |
| return (succ_state == NULL) |
| ? pred_state->Copy(succ_block, pred_block, zone) |
| : succ_state->Merge(succ_block, pred_state, pred_block, zone); |
| } |
| |
| static State* Finish(State* state, HBasicBlock* block, Zone* zone) { |
| if (FLAG_trace_removable_simulates) { |
| PrintF("[preparing state %p for B%d]\n", reinterpret_cast<void*>(state), |
| block->block_id()); |
| } |
| // For our current local analysis, we should not remember simulates across |
| // block boundaries. |
| DCHECK(!state->HasRememberedSimulates()); |
| // Nasty heuristic: Never remove the first simulate in a block. This |
| // just so happens to have a beneficial effect on register allocation. |
| state->first_ = true; |
| return state; |
| } |
| |
| private: |
| explicit State(const State& other) |
| : zone_(other.zone_), |
| mergelist_(other.mergelist_, other.zone_), |
| first_(other.first_), |
| mode_(other.mode_) { } |
| |
| enum Mode { NORMAL, COLLECT_CONSECUTIVE_SIMULATES }; |
| |
| bool HasRememberedSimulates() const { return !mergelist_.is_empty(); } |
| |
| void Remember(HSimulate* sim) { |
| mergelist_.Add(sim, zone_); |
| } |
| |
| void FlushSimulates() { |
| if (HasRememberedSimulates()) { |
| mergelist_.RemoveLast()->MergeWith(&mergelist_); |
| } |
| } |
| |
| void RemoveSimulates() { |
| while (HasRememberedSimulates()) { |
| mergelist_.RemoveLast()->DeleteAndReplaceWith(NULL); |
| } |
| } |
| |
| State* Copy(HBasicBlock* succ_block, HBasicBlock* pred_block, Zone* zone) { |
| State* copy = new(zone) State(*this); |
| if (FLAG_trace_removable_simulates) { |
| PrintF("[copy state %p from B%d to new state %p for B%d]\n", |
| reinterpret_cast<void*>(this), pred_block->block_id(), |
| reinterpret_cast<void*>(copy), succ_block->block_id()); |
| } |
| return copy; |
| } |
| |
| State* Merge(HBasicBlock* succ_block, |
| State* pred_state, |
| HBasicBlock* pred_block, |
| Zone* zone) { |
| // For our current local analysis, we should not remember simulates across |
| // block boundaries. |
| DCHECK(!pred_state->HasRememberedSimulates()); |
| DCHECK(!HasRememberedSimulates()); |
| if (FLAG_trace_removable_simulates) { |
| PrintF("[merge state %p from B%d into %p for B%d]\n", |
| reinterpret_cast<void*>(pred_state), pred_block->block_id(), |
| reinterpret_cast<void*>(this), succ_block->block_id()); |
| } |
| return this; |
| } |
| |
| Zone* zone_; |
| ZoneList<HSimulate*> mergelist_; |
| bool first_; |
| Mode mode_; |
| }; |
| |
| |
| // We don't use effects here. |
| class Effects : public ZoneObject { |
| public: |
| explicit Effects(Zone* zone) { } |
| bool Disabled() { return true; } |
| void Process(HInstruction* instr, Zone* zone) { } |
| void Apply(State* state) { } |
| void Union(Effects* that, Zone* zone) { } |
| }; |
| |
| |
| void HMergeRemovableSimulatesPhase::Run() { |
| HFlowEngine<State, Effects> engine(graph(), zone()); |
| State* state = new(zone()) State(zone()); |
| engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), state); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |