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-random-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 BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count, | |
18 int register_count, Zone* zone) | |
19 : parameter_count_(parameter_count), | |
20 bit_vector_(new (zone) | |
21 BitVector(parameter_count + register_count, zone)) {} | |
22 | |
23 void BytecodeLoopAssignments::Add(interpreter::Register r) { | |
24 if (r.is_parameter()) { | |
25 bit_vector_->Add(r.ToParameterIndex(parameter_count_)); | |
26 } else { | |
27 bit_vector_->Add(parameter_count_ + r.index()); | |
28 } | |
29 } | |
30 | |
31 void BytecodeLoopAssignments::AddPair(interpreter::Register r) { | |
32 if (r.is_parameter()) { | |
33 DCHECK(interpreter::Register(r.index() + 1).is_parameter()); | |
34 bit_vector_->Add(r.ToParameterIndex(parameter_count_)); | |
35 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1); | |
36 } else { | |
37 DCHECK(!interpreter::Register(r.index() + 1).is_parameter()); | |
38 bit_vector_->Add(parameter_count_ + r.index()); | |
39 bit_vector_->Add(parameter_count_ + r.index() + 1); | |
40 } | |
41 } | |
42 | |
43 void BytecodeLoopAssignments::AddTriple(interpreter::Register r) { | |
44 if (r.is_parameter()) { | |
45 DCHECK(interpreter::Register(r.index() + 1).is_parameter()); | |
46 DCHECK(interpreter::Register(r.index() + 2).is_parameter()); | |
47 bit_vector_->Add(r.ToParameterIndex(parameter_count_)); | |
48 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1); | |
49 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 2); | |
50 } else { | |
51 DCHECK(!interpreter::Register(r.index() + 1).is_parameter()); | |
52 DCHECK(!interpreter::Register(r.index() + 2).is_parameter()); | |
53 bit_vector_->Add(parameter_count_ + r.index()); | |
54 bit_vector_->Add(parameter_count_ + r.index() + 1); | |
55 bit_vector_->Add(parameter_count_ + r.index() + 2); | |
56 } | |
57 } | |
58 | |
59 void BytecodeLoopAssignments::AddAll() { bit_vector_->AddAll(); } | |
60 | |
61 void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) { | |
62 bit_vector_->Union(*other.bit_vector_); | |
63 } | |
64 | |
65 bool BytecodeLoopAssignments::ContainsParameter(int index) const { | |
66 DCHECK_GE(index, 0); | |
67 DCHECK_LT(index, parameter_count()); | |
68 return bit_vector_->Contains(index); | |
69 } | |
70 | |
71 bool BytecodeLoopAssignments::ContainsLocal(int index) const { | |
72 DCHECK_GE(index, 0); | |
73 DCHECK_LT(index, local_count()); | |
74 return bit_vector_->Contains(parameter_count_ + index); | |
75 } | |
76 | |
77 bool BytecodeLoopAssignments::ContainsAccumulator() const { | |
78 // TODO(leszeks): This assumes the accumulator is always assigned. This is | |
79 // probably correct, but that assignment is also probably dead, so we should | |
80 // check liveness. | |
81 return true; | |
82 } | |
83 | |
17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, | 84 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, |
18 Zone* zone, bool do_liveness_analysis) | 85 Zone* zone, bool do_liveness_analysis) |
19 : bytecode_array_(bytecode_array), | 86 : bytecode_array_(bytecode_array), |
20 do_liveness_analysis_(do_liveness_analysis), | 87 do_liveness_analysis_(do_liveness_analysis), |
21 zone_(zone), | 88 zone_(zone), |
22 loop_stack_(zone), | 89 loop_stack_(zone), |
23 loop_end_index_queue_(zone), | 90 loop_end_index_queue_(zone), |
24 end_to_header_(zone), | 91 end_to_header_(zone), |
25 header_to_parent_(zone), | 92 header_to_info_(zone), |
26 liveness_map_(bytecode_array->length(), zone) {} | 93 liveness_map_(bytecode_array->length(), zone) {} |
27 | 94 |
28 namespace { | 95 namespace { |
29 | 96 |
30 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness, | 97 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness, |
31 const BytecodeArrayAccessor& accessor) { | 98 const BytecodeArrayAccessor& accessor) { |
32 int num_operands = Bytecodes::NumberOfOperands(bytecode); | 99 int num_operands = Bytecodes::NumberOfOperands(bytecode); |
33 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); | 100 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); |
34 AccumulatorUse accumulator_use = Bytecodes::GetAccumulatorUse(bytecode); | 101 AccumulatorUse accumulator_use = Bytecodes::GetAccumulatorUse(bytecode); |
35 | 102 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 int handler_offset = | 205 int handler_offset = |
139 table->LookupRange(current_offset, &handler_context, nullptr); | 206 table->LookupRange(current_offset, &handler_context, nullptr); |
140 | 207 |
141 if (handler_offset != -1) { | 208 if (handler_offset != -1) { |
142 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset)); | 209 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset)); |
143 out_liveness.MarkRegisterLive(handler_context); | 210 out_liveness.MarkRegisterLive(handler_context); |
144 } | 211 } |
145 } | 212 } |
146 } | 213 } |
147 | 214 |
215 void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments, | |
216 const BytecodeArrayAccessor& accessor) { | |
217 int num_operands = Bytecodes::NumberOfOperands(bytecode); | |
218 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); | |
219 | |
220 for (int i = 0; i < num_operands; ++i) { | |
221 switch (operand_types[i]) { | |
222 case OperandType::kRegOut: { | |
223 assignments.Add(accessor.GetRegisterOperand(i)); | |
224 break; | |
225 } | |
226 case OperandType::kRegOutPair: { | |
227 assignments.AddPair(accessor.GetRegisterOperand(i)); | |
228 break; | |
229 } | |
230 case OperandType::kRegOutTriple: { | |
231 assignments.AddTriple(accessor.GetRegisterOperand(i)); | |
232 break; | |
233 } | |
234 default: | |
235 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i])); | |
236 break; | |
237 } | |
238 } | |
239 } | |
240 | |
148 } // namespace | 241 } // namespace |
149 | 242 |
150 void BytecodeAnalysis::Analyze() { | 243 void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { |
151 loop_stack_.push(-1); | 244 loop_stack_.push({-1, nullptr}); |
152 | 245 |
153 BytecodeLivenessState* next_bytecode_in_liveness = nullptr; | 246 BytecodeLivenessState* next_bytecode_in_liveness = nullptr; |
154 | 247 |
248 bool is_osr_loop = false; | |
249 int osr_loop_end_offset = | |
250 osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt(); | |
251 | |
155 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); | 252 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
156 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { | 253 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { |
157 Bytecode bytecode = iterator.current_bytecode(); | 254 Bytecode bytecode = iterator.current_bytecode(); |
158 int current_offset = iterator.current_offset(); | 255 int current_offset = iterator.current_offset(); |
159 | 256 |
160 if (bytecode == Bytecode::kJumpLoop) { | 257 if (bytecode == Bytecode::kJumpLoop) { |
161 // Every byte up to and including the last byte within the backwards jump | 258 // Every byte up to and including the last byte within the backwards jump |
162 // instruction is considered part of the loop, set loop end accordingly. | 259 // instruction is considered part of the loop, set loop end accordingly. |
163 int loop_end = current_offset + iterator.current_bytecode_size(); | 260 int loop_end = current_offset + iterator.current_bytecode_size(); |
164 PushLoop(iterator.GetJumpTargetOffset(), loop_end); | 261 PushLoop(iterator.GetJumpTargetOffset(), loop_end); |
165 | 262 |
263 // Normally prefixed bytecodes are treated as if the prefix's offset was | |
264 // the actual bytecode's offset. However, the OSR id is the offset of the | |
265 // actual JumpLoop bytecode, so we need to find the location of that | |
266 // bytecode ignoring the prefix. | |
rmcilroy
2016/12/14 10:27:55
Michi question (not for this CL) - should we use t
Michael Starzinger
2016/12/15 13:16:57
Acknowledged. As discussed offline: Doing this bef
| |
267 int jump_loop_offset = current_offset + iterator.current_prefix_offset(); | |
268 is_osr_loop = (jump_loop_offset == osr_loop_end_offset); | |
269 | |
270 #if DEBUG | |
271 // Check that is_osr_loop is set iff the osr_loop_end_offset is within | |
272 // this bytecode. | |
273 if (is_osr_loop) { | |
274 DCHECK(current_offset <= osr_loop_end_offset && | |
275 osr_loop_end_offset < loop_end); | |
276 } else { | |
277 DCHECK(current_offset > osr_loop_end_offset || | |
278 osr_loop_end_offset >= loop_end); | |
279 } | |
rmcilroy
2016/12/14 10:27:55
Could you just pull this out into a separate helpe
Leszek Swirski
2016/12/14 14:42:19
Done.
| |
280 #endif | |
281 | |
282 if (is_osr_loop) { | |
283 loop_stack_.top().loop_info->assignments().AddAll(); | |
rmcilroy
2016/12/14 10:27:55
Could you add a comment on why this is necessary
Leszek Swirski
2016/12/14 14:42:18
Done.
| |
284 } | |
285 | |
166 // Save the index so that we can do another pass later. | 286 // Save the index so that we can do another pass later. |
167 if (do_liveness_analysis_) { | 287 if (do_liveness_analysis_) { |
168 loop_end_index_queue_.push_back(iterator.current_index()); | 288 loop_end_index_queue_.push_back(iterator.current_index()); |
169 } | 289 } |
170 } else if (current_offset == loop_stack_.top()) { | 290 } else if (loop_stack_.size() > 1) { |
171 loop_stack_.pop(); | 291 LoopStackEntry& current_loop = loop_stack_.top(); |
292 LoopInfo* current_loop_info = current_loop.loop_info; | |
293 | |
294 if (!is_osr_loop) { | |
rmcilroy
2016/12/14 10:27:55
Do we need this check here? If we AddAll for the O
Leszek Swirski
2016/12/14 14:42:19
Sure, it's not performance critical I think.
| |
295 // TODO(leszeks): Ideally, we'd only set values that were assigned in | |
296 // the loop *and* are live when the loop exits. However, this requires | |
297 // tracking the out-liveness of *all* loop exits, which is not | |
298 // information we currently have. | |
299 UpdateAssignments(bytecode, current_loop_info->assignments(), iterator); | |
300 } | |
301 | |
302 if (current_offset == current_loop.header_offset) { | |
303 loop_stack_.pop(); | |
304 if (loop_stack_.size() > 1) { | |
305 // Propagate inner loop assignments to outer loop. | |
306 loop_stack_.top().loop_info->assignments().Union( | |
307 current_loop_info->assignments()); | |
308 } | |
309 } | |
172 } | 310 } |
173 | 311 |
174 if (do_liveness_analysis_) { | 312 if (do_liveness_analysis_) { |
175 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( | 313 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( |
176 current_offset, bytecode_array()->register_count(), zone()); | 314 current_offset, bytecode_array()->register_count(), zone()); |
177 | 315 |
178 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, | 316 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, |
179 iterator, liveness_map_); | 317 iterator, liveness_map_); |
180 liveness.in->CopyFrom(*liveness.out); | 318 liveness.in->CopyFrom(*liveness.out); |
181 UpdateInLiveness(bytecode, *liveness.in, iterator); | 319 UpdateInLiveness(bytecode, *liveness.in, iterator); |
182 | 320 |
183 next_bytecode_in_liveness = liveness.in; | 321 next_bytecode_in_liveness = liveness.in; |
184 } | 322 } |
185 } | 323 } |
186 | 324 |
187 DCHECK_EQ(loop_stack_.size(), 1u); | 325 DCHECK_EQ(loop_stack_.size(), 1u); |
188 DCHECK_EQ(loop_stack_.top(), -1); | 326 DCHECK_EQ(loop_stack_.top().header_offset, -1); |
189 | 327 |
190 if (!do_liveness_analysis_) return; | 328 if (!do_liveness_analysis_) return; |
191 | 329 |
192 // At this point, every bytecode has a valid in and out liveness, except for | 330 // At this point, every bytecode has a valid in and out liveness, except for |
193 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness | 331 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness |
194 // analysis iterations can only add additional liveness bits that are pulled | 332 // analysis iterations can only add additional liveness bits that are pulled |
195 // across these back edges. | 333 // across these back edges. |
196 // | 334 // |
197 // Furthermore, a loop header's in-liveness can only change based on any | 335 // Furthermore, a loop header's in-liveness can only change based on any |
198 // bytecodes *after* the loop end -- it cannot change as a result of the | 336 // bytecodes *after* the loop end -- it cannot change as a result of the |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
252 // can't change, we need only to update the out-liveness. | 390 // can't change, we need only to update the out-liveness. |
253 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, | 391 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, |
254 next_bytecode_in_liveness, iterator, liveness_map_); | 392 next_bytecode_in_liveness, iterator, liveness_map_); |
255 } | 393 } |
256 | 394 |
257 DCHECK(LivenessIsValid()); | 395 DCHECK(LivenessIsValid()); |
258 } | 396 } |
259 | 397 |
260 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { | 398 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { |
261 DCHECK(loop_header < loop_end); | 399 DCHECK(loop_header < loop_end); |
262 DCHECK(loop_stack_.top() < loop_header); | 400 DCHECK(loop_stack_.top().header_offset < loop_header); |
263 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); | 401 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); |
264 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); | 402 DCHECK(header_to_info_.find(loop_header) == header_to_info_.end()); |
265 | 403 |
266 end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header)); | 404 int parent_offset = loop_stack_.top().header_offset; |
267 header_to_parent_.insert( | 405 |
268 ZoneMap<int, int>::value_type(loop_header, loop_stack_.top())); | 406 end_to_header_.insert({loop_end, loop_header}); |
269 loop_stack_.push(loop_header); | 407 auto it = header_to_info_.insert( |
408 {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(), | |
409 bytecode_array_->register_count(), zone_)}); | |
410 // Get the loop info pointer from the output of insert. | |
411 LoopInfo* loop_info = &it.first->second; | |
412 | |
413 loop_stack_.push({loop_header, loop_info}); | |
270 } | 414 } |
271 | 415 |
272 bool BytecodeAnalysis::IsLoopHeader(int offset) const { | 416 bool BytecodeAnalysis::IsLoopHeader(int offset) const { |
273 return header_to_parent_.find(offset) != header_to_parent_.end(); | 417 return header_to_info_.find(offset) != header_to_info_.end(); |
274 } | 418 } |
275 | 419 |
276 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { | 420 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { |
277 auto loop_end_to_header = end_to_header_.upper_bound(offset); | 421 auto loop_end_to_header = end_to_header_.upper_bound(offset); |
278 // If there is no next end => offset is not in a loop. | 422 // If there is no next end => offset is not in a loop. |
279 if (loop_end_to_header == end_to_header_.end()) { | 423 if (loop_end_to_header == end_to_header_.end()) { |
280 return -1; | 424 return -1; |
281 } | 425 } |
282 // If the header preceeds the offset, this is the loop | 426 // If the header preceeds the offset, this is the loop |
283 // | 427 // |
284 // .> header <--loop_end_to_header | 428 // .> header <--loop_end_to_header |
285 // | | 429 // | |
286 // | <--offset | 430 // | <--offset |
287 // | | 431 // | |
288 // `- end | 432 // `- end |
289 if (loop_end_to_header->second <= offset) { | 433 if (loop_end_to_header->second <= offset) { |
290 return loop_end_to_header->second; | 434 return loop_end_to_header->second; |
291 } | 435 } |
292 // Otherwise there is a (potentially nested) loop after this offset. | 436 // Otherwise there is a (potentially nested) loop after this offset. |
293 // | 437 // |
294 // <--offset | 438 // <--offset |
295 // | 439 // |
296 // .> header | 440 // .> header |
297 // | | 441 // | |
298 // | .> header <--loop_end_to_header | 442 // | .> header <--loop_end_to_header |
299 // | | | 443 // | | |
300 // | `- end | 444 // | `- end |
301 // | | 445 // | |
302 // `- end | 446 // `- end |
303 // We just return the parent of the next loop header (might be -1). | 447 // We just return the parent of the next loop (might be -1). |
304 DCHECK(header_to_parent_.upper_bound(offset) != header_to_parent_.end()); | 448 DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end()); |
305 | 449 |
306 return header_to_parent_.upper_bound(offset)->second; | 450 return header_to_info_.upper_bound(offset)->second.parent_offset(); |
307 } | 451 } |
308 | 452 |
309 int BytecodeAnalysis::GetParentLoopFor(int header_offset) const { | 453 const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const { |
310 DCHECK(IsLoopHeader(header_offset)); | 454 DCHECK(IsLoopHeader(header_offset)); |
311 | 455 |
312 return header_to_parent_.find(header_offset)->second; | 456 return header_to_info_.find(header_offset)->second; |
313 } | 457 } |
314 | 458 |
315 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( | 459 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( |
316 int offset) const { | 460 int offset) const { |
317 if (!do_liveness_analysis_) return nullptr; | 461 if (!do_liveness_analysis_) return nullptr; |
318 | 462 |
319 return liveness_map_.GetInLiveness(offset); | 463 return liveness_map_.GetInLiveness(offset); |
320 } | 464 } |
321 | 465 |
322 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( | 466 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
477 } | 621 } |
478 } | 622 } |
479 | 623 |
480 return invalid_offset == -1; | 624 return invalid_offset == -1; |
481 } | 625 } |
482 #endif | 626 #endif |
483 | 627 |
484 } // namespace compiler | 628 } // namespace compiler |
485 } // namespace internal | 629 } // namespace internal |
486 } // namespace v8 | 630 } // namespace v8 |
OLD | NEW |