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