Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1 | // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "src/hydrogen-flow-engine.h" |
| 6 | #include "src/hydrogen-instructions.h" |
| 7 | #include "src/hydrogen-removable-simulates.h" |
| 8 | |
| 9 | namespace v8 { |
| 10 | namespace internal { |
| 11 | |
| 12 | class State : public ZoneObject { |
| 13 | public: |
| 14 | explicit State(Zone* zone) |
| 15 | : zone_(zone), mergelist_(2, zone), first_(true), mode_(NORMAL) { } |
| 16 | |
| 17 | State* Process(HInstruction* instr, Zone* zone) { |
| 18 | if (FLAG_trace_removable_simulates) { |
| 19 | PrintF("[%s with state %p in B%d: #%d %s]\n", |
| 20 | mode_ == NORMAL ? "processing" : "collecting", |
| 21 | reinterpret_cast<void*>(this), instr->block()->block_id(), |
| 22 | instr->id(), instr->Mnemonic()); |
| 23 | } |
| 24 | // Forward-merge "trains" of simulates after an instruction with observable |
| 25 | // side effects to keep live ranges short. |
| 26 | if (mode_ == COLLECT_CONSECUTIVE_SIMULATES) { |
| 27 | if (instr->IsSimulate()) { |
| 28 | HSimulate* current_simulate = HSimulate::cast(instr); |
| 29 | if (current_simulate->is_candidate_for_removal() && |
| 30 | !current_simulate->ast_id().IsNone()) { |
| 31 | Remember(current_simulate); |
| 32 | return this; |
| 33 | } |
| 34 | } |
| 35 | FlushSimulates(); |
| 36 | mode_ = NORMAL; |
| 37 | } |
| 38 | // Ensure there's a non-foldable HSimulate before an HEnterInlined to avoid |
| 39 | // folding across HEnterInlined. |
| 40 | DCHECK(!(instr->IsEnterInlined() && |
| 41 | HSimulate::cast(instr->previous())->is_candidate_for_removal())); |
| 42 | if (instr->IsLeaveInlined() || instr->IsReturn()) { |
| 43 | // Never fold simulates from inlined environments into simulates in the |
| 44 | // outer environment. Simply remove all accumulated simulates without |
| 45 | // merging. This is safe because simulates after instructions with side |
| 46 | // effects are never added to the merge list. The same reasoning holds for |
| 47 | // return instructions. |
| 48 | RemoveSimulates(); |
| 49 | return this; |
| 50 | } |
| 51 | if (instr->IsControlInstruction()) { |
| 52 | // Merge the accumulated simulates at the end of the block. |
| 53 | FlushSimulates(); |
| 54 | return this; |
| 55 | } |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 56 | if (instr->IsCapturedObject()) { |
| 57 | // Do not merge simulates across captured objects - captured objects |
| 58 | // change environments during environment replay, and such changes |
| 59 | // would not be reflected in the simulate. |
| 60 | FlushSimulates(); |
| 61 | return this; |
| 62 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 63 | // Skip the non-simulates and the first simulate. |
| 64 | if (!instr->IsSimulate()) return this; |
| 65 | if (first_) { |
| 66 | first_ = false; |
| 67 | return this; |
| 68 | } |
| 69 | HSimulate* current_simulate = HSimulate::cast(instr); |
| 70 | if (!current_simulate->is_candidate_for_removal()) { |
| 71 | Remember(current_simulate); |
| 72 | FlushSimulates(); |
| 73 | } else if (current_simulate->ast_id().IsNone()) { |
| 74 | DCHECK(current_simulate->next()->IsEnterInlined()); |
| 75 | FlushSimulates(); |
| 76 | } else if (current_simulate->previous()->HasObservableSideEffects()) { |
| 77 | Remember(current_simulate); |
| 78 | mode_ = COLLECT_CONSECUTIVE_SIMULATES; |
| 79 | } else { |
| 80 | Remember(current_simulate); |
| 81 | } |
| 82 | |
| 83 | return this; |
| 84 | } |
| 85 | |
| 86 | static State* Merge(State* succ_state, |
| 87 | HBasicBlock* succ_block, |
| 88 | State* pred_state, |
| 89 | HBasicBlock* pred_block, |
| 90 | Zone* zone) { |
| 91 | return (succ_state == NULL) |
| 92 | ? pred_state->Copy(succ_block, pred_block, zone) |
| 93 | : succ_state->Merge(succ_block, pred_state, pred_block, zone); |
| 94 | } |
| 95 | |
| 96 | static State* Finish(State* state, HBasicBlock* block, Zone* zone) { |
| 97 | if (FLAG_trace_removable_simulates) { |
| 98 | PrintF("[preparing state %p for B%d]\n", reinterpret_cast<void*>(state), |
| 99 | block->block_id()); |
| 100 | } |
| 101 | // For our current local analysis, we should not remember simulates across |
| 102 | // block boundaries. |
| 103 | DCHECK(!state->HasRememberedSimulates()); |
| 104 | // Nasty heuristic: Never remove the first simulate in a block. This |
| 105 | // just so happens to have a beneficial effect on register allocation. |
| 106 | state->first_ = true; |
| 107 | return state; |
| 108 | } |
| 109 | |
| 110 | private: |
| 111 | explicit State(const State& other) |
| 112 | : zone_(other.zone_), |
| 113 | mergelist_(other.mergelist_, other.zone_), |
| 114 | first_(other.first_), |
| 115 | mode_(other.mode_) { } |
| 116 | |
| 117 | enum Mode { NORMAL, COLLECT_CONSECUTIVE_SIMULATES }; |
| 118 | |
| 119 | bool HasRememberedSimulates() const { return !mergelist_.is_empty(); } |
| 120 | |
| 121 | void Remember(HSimulate* sim) { |
| 122 | mergelist_.Add(sim, zone_); |
| 123 | } |
| 124 | |
| 125 | void FlushSimulates() { |
| 126 | if (HasRememberedSimulates()) { |
| 127 | mergelist_.RemoveLast()->MergeWith(&mergelist_); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void RemoveSimulates() { |
| 132 | while (HasRememberedSimulates()) { |
| 133 | mergelist_.RemoveLast()->DeleteAndReplaceWith(NULL); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | State* Copy(HBasicBlock* succ_block, HBasicBlock* pred_block, Zone* zone) { |
| 138 | State* copy = new(zone) State(*this); |
| 139 | if (FLAG_trace_removable_simulates) { |
| 140 | PrintF("[copy state %p from B%d to new state %p for B%d]\n", |
| 141 | reinterpret_cast<void*>(this), pred_block->block_id(), |
| 142 | reinterpret_cast<void*>(copy), succ_block->block_id()); |
| 143 | } |
| 144 | return copy; |
| 145 | } |
| 146 | |
| 147 | State* Merge(HBasicBlock* succ_block, |
| 148 | State* pred_state, |
| 149 | HBasicBlock* pred_block, |
| 150 | Zone* zone) { |
| 151 | // For our current local analysis, we should not remember simulates across |
| 152 | // block boundaries. |
| 153 | DCHECK(!pred_state->HasRememberedSimulates()); |
| 154 | DCHECK(!HasRememberedSimulates()); |
| 155 | if (FLAG_trace_removable_simulates) { |
| 156 | PrintF("[merge state %p from B%d into %p for B%d]\n", |
| 157 | reinterpret_cast<void*>(pred_state), pred_block->block_id(), |
| 158 | reinterpret_cast<void*>(this), succ_block->block_id()); |
| 159 | } |
| 160 | return this; |
| 161 | } |
| 162 | |
| 163 | Zone* zone_; |
| 164 | ZoneList<HSimulate*> mergelist_; |
| 165 | bool first_; |
| 166 | Mode mode_; |
| 167 | }; |
| 168 | |
| 169 | |
| 170 | // We don't use effects here. |
| 171 | class Effects : public ZoneObject { |
| 172 | public: |
| 173 | explicit Effects(Zone* zone) { } |
| 174 | bool Disabled() { return true; } |
| 175 | void Process(HInstruction* instr, Zone* zone) { } |
| 176 | void Apply(State* state) { } |
| 177 | void Union(Effects* that, Zone* zone) { } |
| 178 | }; |
| 179 | |
| 180 | |
| 181 | void HMergeRemovableSimulatesPhase::Run() { |
| 182 | HFlowEngine<State, Effects> engine(graph(), zone()); |
| 183 | State* state = new(zone()) State(zone()); |
| 184 | engine.AnalyzeDominatedBlocks(graph()->blocks()->at(0), state); |
| 185 | } |
| 186 | |
| 187 | } } // namespace v8::internal |