OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/bytecode-analysis.h" | 5 #include "src/compiler/bytecode-analysis.h" |
6 | 6 |
7 #include "src/interpreter/bytecode-array-iterator.h" | 7 #include "src/interpreter/bytecode-array-iterator.h" |
8 #include "src/interpreter/bytecode-array-reverse-iterator.h" | 8 #include "src/interpreter/bytecode-array-random-iterator.h" |
9 #include "src/objects-inl.h" | 9 #include "src/objects-inl.h" |
10 | 10 |
11 namespace v8 { | 11 namespace v8 { |
12 namespace internal { | 12 namespace internal { |
13 namespace compiler { | 13 namespace compiler { |
14 | 14 |
15 using namespace interpreter; | 15 using namespace interpreter; |
16 | 16 |
17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, | 17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, |
18 Zone* zone, bool do_liveness_analysis) | 18 Zone* zone, bool do_liveness_analysis) |
19 : bytecode_array_(bytecode_array), | 19 : bytecode_array_(bytecode_array), |
20 do_liveness_analysis_(do_liveness_analysis), | 20 do_liveness_analysis_(do_liveness_analysis), |
21 zone_(zone), | 21 zone_(zone), |
22 loop_stack_(zone), | 22 loop_stack_(zone), |
| 23 loop_end_index_queue_(zone), |
23 end_to_header_(zone), | 24 end_to_header_(zone), |
24 header_to_parent_(zone), | 25 header_to_parent_(zone), |
25 liveness_map_(bytecode_array->length(), zone) {} | 26 liveness_map_(bytecode_array->length(), zone) {} |
26 | 27 |
27 namespace { | 28 namespace { |
28 | 29 |
29 void UpdateInLiveness(Bytecode bytecode, BitVector& in_liveness, | 30 void UpdateInLiveness(Bytecode bytecode, BitVector& in_liveness, |
30 const BytecodeArrayAccessor& accessor) { | 31 const BytecodeArrayAccessor& accessor) { |
31 int num_operands = Bytecodes::NumberOfOperands(bytecode); | 32 int num_operands = Bytecodes::NumberOfOperands(bytecode); |
32 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); | 33 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 out_liveness.Add(handler_context); | 142 out_liveness.Add(handler_context); |
142 } | 143 } |
143 } | 144 } |
144 } | 145 } |
145 | 146 |
146 } // namespace | 147 } // namespace |
147 | 148 |
148 void BytecodeAnalysis::Analyze() { | 149 void BytecodeAnalysis::Analyze() { |
149 loop_stack_.push(-1); | 150 loop_stack_.push(-1); |
150 | 151 |
151 // The last JumpLoop that we haven't done a guaranteed valid liveness pass | 152 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
152 // over. See the below wall of text for a more thorough explanation. | 153 for (iterator.GoToEnd(); iterator.Valid(); --iterator) { |
153 int last_invalid_jumploop_offset = -1; | |
154 | |
155 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | |
156 for (; !iterator.done(); iterator.Advance()) { | |
157 Bytecode bytecode = iterator.current_bytecode(); | 154 Bytecode bytecode = iterator.current_bytecode(); |
158 int current_offset = iterator.current_offset(); | 155 int current_offset = iterator.current_offset(); |
159 | 156 |
160 if (bytecode == Bytecode::kJumpLoop) { | 157 if (bytecode == Bytecode::kJumpLoop) { |
161 PushLoop(iterator.GetJumpTargetOffset(), current_offset); | 158 PushLoop(iterator.GetJumpTargetOffset(), current_offset); |
162 | 159 |
163 // Save the last offset so that we can do another pass later. | 160 // Save the index so that we can do another pass later. |
164 if (last_invalid_jumploop_offset == -1) { | 161 if (do_liveness_analysis_) { |
165 last_invalid_jumploop_offset = current_offset; | 162 loop_end_index_queue_.push_back(iterator.current_index()); |
166 } | 163 } |
167 } else if (current_offset == loop_stack_.top()) { | 164 } else if (current_offset == loop_stack_.top()) { |
168 loop_stack_.pop(); | 165 loop_stack_.pop(); |
169 } | 166 } |
170 | 167 |
171 if (do_liveness_analysis_) { | 168 if (do_liveness_analysis_) { |
172 // The liveness vector had bits for the liveness of the registers, and one | 169 // The liveness vector had bits for the liveness of the registers, and one |
173 // more bit for the liveness of the accumulator. | 170 // more bit for the liveness of the accumulator. |
174 Liveness& liveness = liveness_map_.InitializeLiveness( | 171 Liveness& liveness = liveness_map_.InitializeLiveness( |
175 current_offset, bytecode_array()->register_count() + 1, zone()); | 172 current_offset, bytecode_array()->register_count() + 1, zone()); |
(...skipping 22 matching lines...) Expand all Loading... |
198 // So, if we know that the liveness of bytecodes after a loop header won't | 195 // So, if we know that the liveness of bytecodes after a loop header won't |
199 // change (e.g. because there are no loops in them, or we have already ensured | 196 // change (e.g. because there are no loops in them, or we have already ensured |
200 // those loops are valid), we can safely update the loop end and pass over the | 197 // those loops are valid), we can safely update the loop end and pass over the |
201 // loop body, and then never have to pass over that loop end again, because we | 198 // loop body, and then never have to pass over that loop end again, because we |
202 // have shown that its target, the loop header, can't change from the entries | 199 // have shown that its target, the loop header, can't change from the entries |
203 // after the loop, and can't change from any loop body pass. | 200 // after the loop, and can't change from any loop body pass. |
204 // | 201 // |
205 // This means that in a pass, we can iterate backwards over the bytecode | 202 // This means that in a pass, we can iterate backwards over the bytecode |
206 // array, process any loops that we encounter, and on subsequent passes we can | 203 // array, process any loops that we encounter, and on subsequent passes we can |
207 // skip processing those loops (though we still have to process inner loops). | 204 // skip processing those loops (though we still have to process inner loops). |
| 205 // |
| 206 // Equivalently, we can queue up loop ends from back to front, and pass over |
| 207 // the loops in that order, as this preserves both the bottom-to-top and |
| 208 // outer-to-inner requirements. |
208 | 209 |
209 while (last_invalid_jumploop_offset != -1) { | 210 for (int loop_end_index : loop_end_index_queue_) { |
210 // TODO(leszeks): We shouldn't need to iterate here, we should just have a | 211 iterator.GoToIndex(loop_end_index); |
211 // random access iterator. | |
212 iterator.Reset(); | |
213 while (last_invalid_jumploop_offset < iterator.current_offset()) { | |
214 iterator.Advance(); | |
215 } | |
216 last_invalid_jumploop_offset = -1; | |
217 | 212 |
218 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); | 213 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); |
219 | 214 |
220 for (; !iterator.done(); iterator.Advance()) { | 215 int header_offset = iterator.GetJumpTargetOffset(); |
| 216 int end_offset = iterator.current_offset(); |
| 217 |
| 218 Liveness& header_liveness = liveness_map_.GetLiveness(header_offset); |
| 219 Liveness& end_liveness = liveness_map_.GetLiveness(end_offset); |
| 220 |
| 221 if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) { |
| 222 // Only update the loop body if the loop end liveness changed. |
| 223 continue; |
| 224 } |
| 225 end_liveness.in->CopyFrom(*end_liveness.out); |
| 226 |
| 227 // Advance into the loop body. |
| 228 --iterator; |
| 229 for (; iterator.current_offset() > header_offset; --iterator) { |
221 Bytecode bytecode = iterator.current_bytecode(); | 230 Bytecode bytecode = iterator.current_bytecode(); |
222 if (bytecode != Bytecode::kJumpLoop) { | |
223 // Skip bytecodes until we hit a JumpLoop. This check isn't needed for | |
224 // the first loop we see (thanks to saving its offset), but it is for | |
225 // subsequent ones we want to process on this pass. | |
226 continue; | |
227 } | |
228 | 231 |
229 int header_offset = iterator.GetJumpTargetOffset(); | 232 int current_offset = iterator.current_offset(); |
230 int end_offset = iterator.current_offset(); | 233 Liveness& liveness = liveness_map_.GetLiveness(current_offset); |
231 | 234 |
232 Liveness& header_liveness = liveness_map_.GetLiveness(header_offset); | 235 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); |
233 Liveness& end_liveness = liveness_map_.GetLiveness(end_offset); | 236 liveness.in->CopyFrom(*liveness.out); |
234 | 237 UpdateInLiveness(bytecode, *liveness.in, iterator); |
235 if (end_liveness.out->UnionIsChanged(*header_liveness.in)) { | |
236 // Only update the loop body if the loop end liveness changed. | |
237 end_liveness.in->CopyFrom(*end_liveness.out); | |
238 | |
239 // Advance into the loop body. | |
240 iterator.Advance(); | |
241 for (; iterator.current_offset() > header_offset; iterator.Advance()) { | |
242 bytecode = iterator.current_bytecode(); | |
243 if (bytecode == Bytecode::kJumpLoop) { | |
244 // We can't validate this loop at the moment because we can't | |
245 // guarantee that its header is valid yet. Save it for later. | |
246 if (last_invalid_jumploop_offset == -1) { | |
247 last_invalid_jumploop_offset = iterator.current_offset(); | |
248 } | |
249 } | |
250 | |
251 int current_offset = iterator.current_offset(); | |
252 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | |
253 | |
254 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | |
255 liveness.in->CopyFrom(*liveness.out); | |
256 UpdateInLiveness(bytecode, *liveness.in, iterator); | |
257 } | |
258 // Now we are at the loop header. Since the in-liveness of the header | |
259 // can't change, we need only to update the out-liveness. | |
260 bytecode = iterator.current_bytecode(); | |
261 UpdateOutLiveness(bytecode, *header_liveness.out, iterator, | |
262 liveness_map_); | |
263 } | |
264 | |
265 // Keep the iterator going so that we can find other loops. | |
266 } | 238 } |
| 239 // Now we are at the loop header. Since the in-liveness of the header |
| 240 // can't change, we need only to update the out-liveness. |
| 241 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, |
| 242 iterator, liveness_map_); |
267 } | 243 } |
268 | 244 |
269 DCHECK(LivenessIsValid()); | 245 DCHECK(LivenessIsValid()); |
270 } | 246 } |
271 | 247 |
272 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { | 248 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { |
273 DCHECK(loop_header < loop_end); | 249 DCHECK(loop_header < loop_end); |
274 DCHECK(loop_stack_.top() < loop_header); | 250 DCHECK(loop_stack_.top() < loop_header); |
275 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); | 251 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); |
276 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); | 252 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 | 332 |
357 os << " | " << current_offset << ": "; | 333 os << " | " << current_offset << ": "; |
358 iterator.PrintTo(os) << std::endl; | 334 iterator.PrintTo(os) << std::endl; |
359 } | 335 } |
360 | 336 |
361 return os; | 337 return os; |
362 } | 338 } |
363 | 339 |
364 #if DEBUG | 340 #if DEBUG |
365 bool BytecodeAnalysis::LivenessIsValid() { | 341 bool BytecodeAnalysis::LivenessIsValid() { |
366 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | 342 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
367 | 343 |
368 BitVector previous_liveness(bytecode_array()->register_count() + 1, zone()); | 344 BitVector previous_liveness(bytecode_array()->register_count() + 1, zone()); |
369 | 345 |
370 int invalid_offset = -1; | 346 int invalid_offset = -1; |
371 int which_invalid = -1; | 347 int which_invalid = -1; |
372 | 348 |
373 // Ensure that there are no liveness changes if we iterate one more time. | 349 // Ensure that there are no liveness changes if we iterate one more time. |
374 for (iterator.Reset(); !iterator.done(); iterator.Advance()) { | 350 for (iterator.GoToEnd(); iterator.Valid(); --iterator) { |
375 Bytecode bytecode = iterator.current_bytecode(); | 351 Bytecode bytecode = iterator.current_bytecode(); |
376 | 352 |
377 int current_offset = iterator.current_offset(); | 353 int current_offset = iterator.current_offset(); |
378 | 354 |
379 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | 355 Liveness& liveness = liveness_map_.GetLiveness(current_offset); |
380 | 356 |
381 previous_liveness.CopyFrom(*liveness.out); | 357 previous_liveness.CopyFrom(*liveness.out); |
382 | 358 |
383 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | 359 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); |
384 // UpdateOutLiveness skips kJumpLoop, so we update it manually. | 360 // UpdateOutLiveness skips kJumpLoop, so we update it manually. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 } | 453 } |
478 } | 454 } |
479 | 455 |
480 return invalid_offset == -1; | 456 return invalid_offset == -1; |
481 } | 457 } |
482 #endif | 458 #endif |
483 | 459 |
484 } // namespace compiler | 460 } // namespace compiler |
485 } // namespace internal | 461 } // namespace internal |
486 } // namespace v8 | 462 } // namespace v8 |
OLD | NEW |