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:
State(Zone * zone)16 explicit State(Zone* zone)
17 : zone_(zone), mergelist_(2, zone), first_(true), mode_(NORMAL) { }
18
Process(HInstruction * instr,Zone * zone)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
Merge(State * succ_state,HBasicBlock * succ_block,State * pred_state,HBasicBlock * pred_block,Zone * zone)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
Finish(State * state,HBasicBlock * block,Zone * zone)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:
State(const State & other)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
HasRememberedSimulates() const121 bool HasRememberedSimulates() const { return !mergelist_.is_empty(); }
122
Remember(HSimulate * sim)123 void Remember(HSimulate* sim) {
124 mergelist_.Add(sim, zone_);
125 }
126
FlushSimulates()127 void FlushSimulates() {
128 if (HasRememberedSimulates()) {
129 mergelist_.RemoveLast()->MergeWith(&mergelist_);
130 }
131 }
132
RemoveSimulates()133 void RemoveSimulates() {
134 while (HasRememberedSimulates()) {
135 mergelist_.RemoveLast()->DeleteAndReplaceWith(NULL);
136 }
137 }
138
Copy(HBasicBlock * succ_block,HBasicBlock * pred_block,Zone * zone)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
Merge(HBasicBlock * succ_block,State * pred_state,HBasicBlock * pred_block,Zone * zone)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:
Effects(Zone * zone)175 explicit Effects(Zone* zone) { }
Disabled()176 bool Disabled() { return true; }
Process(HInstruction * instr,Zone * zone)177 void Process(HInstruction* instr, Zone* zone) { }
Apply(State * state)178 void Apply(State* state) { }
Union(Effects * that,Zone * zone)179 void Union(Effects* that, Zone* zone) { }
180 };
181
182
Run()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