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 int osr_loop_end_offset = |
| 249 osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt(); |
| 250 |
155 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); | 251 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
156 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { | 252 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { |
157 Bytecode bytecode = iterator.current_bytecode(); | 253 Bytecode bytecode = iterator.current_bytecode(); |
158 int current_offset = iterator.current_offset(); | 254 int current_offset = iterator.current_offset(); |
159 | 255 |
160 if (bytecode == Bytecode::kJumpLoop) { | 256 if (bytecode == Bytecode::kJumpLoop) { |
161 // Every byte up to and including the last byte within the backwards jump | 257 // 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. | 258 // instruction is considered part of the loop, set loop end accordingly. |
163 int loop_end = current_offset + iterator.current_bytecode_size(); | 259 int loop_end = current_offset + iterator.current_bytecode_size(); |
164 PushLoop(iterator.GetJumpTargetOffset(), loop_end); | 260 PushLoop(iterator.GetJumpTargetOffset(), loop_end); |
165 | 261 |
| 262 // Normally prefixed bytecodes are treated as if the prefix's offset was |
| 263 // the actual bytecode's offset. However, the OSR id is the offset of the |
| 264 // actual JumpLoop bytecode, so we need to find the location of that |
| 265 // bytecode ignoring the prefix. |
| 266 int jump_loop_offset = current_offset + iterator.current_prefix_offset(); |
| 267 bool is_osr_loop = (jump_loop_offset == osr_loop_end_offset); |
| 268 |
| 269 // Check that is_osr_loop is set iff the osr_loop_end_offset is within |
| 270 // this bytecode. |
| 271 DCHECK(!is_osr_loop || |
| 272 iterator.OffsetWithinBytecode(osr_loop_end_offset)); |
| 273 |
| 274 // OSR "assigns" everything to OSR values on entry into an OSR loop, so we |
| 275 // need to make sure to considered everything to be assigned. |
| 276 if (is_osr_loop) { |
| 277 loop_stack_.top().loop_info->assignments().AddAll(); |
| 278 } |
| 279 |
166 // Save the index so that we can do another pass later. | 280 // Save the index so that we can do another pass later. |
167 if (do_liveness_analysis_) { | 281 if (do_liveness_analysis_) { |
168 loop_end_index_queue_.push_back(iterator.current_index()); | 282 loop_end_index_queue_.push_back(iterator.current_index()); |
169 } | 283 } |
170 } else if (current_offset == loop_stack_.top()) { | 284 } else if (loop_stack_.size() > 1) { |
171 loop_stack_.pop(); | 285 LoopStackEntry& current_loop = loop_stack_.top(); |
| 286 LoopInfo* current_loop_info = current_loop.loop_info; |
| 287 |
| 288 // TODO(leszeks): Ideally, we'd only set values that were assigned in |
| 289 // the loop *and* are live when the loop exits. However, this requires |
| 290 // tracking the out-liveness of *all* loop exits, which is not |
| 291 // information we currently have. |
| 292 UpdateAssignments(bytecode, current_loop_info->assignments(), iterator); |
| 293 |
| 294 if (current_offset == current_loop.header_offset) { |
| 295 loop_stack_.pop(); |
| 296 if (loop_stack_.size() > 1) { |
| 297 // Propagate inner loop assignments to outer loop. |
| 298 loop_stack_.top().loop_info->assignments().Union( |
| 299 current_loop_info->assignments()); |
| 300 } |
| 301 } |
172 } | 302 } |
173 | 303 |
174 if (do_liveness_analysis_) { | 304 if (do_liveness_analysis_) { |
175 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( | 305 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( |
176 current_offset, bytecode_array()->register_count(), zone()); | 306 current_offset, bytecode_array()->register_count(), zone()); |
177 | 307 |
178 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, | 308 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, |
179 iterator, liveness_map_); | 309 iterator, liveness_map_); |
180 liveness.in->CopyFrom(*liveness.out); | 310 liveness.in->CopyFrom(*liveness.out); |
181 UpdateInLiveness(bytecode, *liveness.in, iterator); | 311 UpdateInLiveness(bytecode, *liveness.in, iterator); |
182 | 312 |
183 next_bytecode_in_liveness = liveness.in; | 313 next_bytecode_in_liveness = liveness.in; |
184 } | 314 } |
185 } | 315 } |
186 | 316 |
187 DCHECK_EQ(loop_stack_.size(), 1u); | 317 DCHECK_EQ(loop_stack_.size(), 1u); |
188 DCHECK_EQ(loop_stack_.top(), -1); | 318 DCHECK_EQ(loop_stack_.top().header_offset, -1); |
189 | 319 |
190 if (!do_liveness_analysis_) return; | 320 if (!do_liveness_analysis_) return; |
191 | 321 |
192 // At this point, every bytecode has a valid in and out liveness, except for | 322 // 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 | 323 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness |
194 // analysis iterations can only add additional liveness bits that are pulled | 324 // analysis iterations can only add additional liveness bits that are pulled |
195 // across these back edges. | 325 // across these back edges. |
196 // | 326 // |
197 // Furthermore, a loop header's in-liveness can only change based on any | 327 // 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 | 328 // 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. | 382 // can't change, we need only to update the out-liveness. |
253 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, | 383 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, |
254 next_bytecode_in_liveness, iterator, liveness_map_); | 384 next_bytecode_in_liveness, iterator, liveness_map_); |
255 } | 385 } |
256 | 386 |
257 DCHECK(LivenessIsValid()); | 387 DCHECK(LivenessIsValid()); |
258 } | 388 } |
259 | 389 |
260 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { | 390 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { |
261 DCHECK(loop_header < loop_end); | 391 DCHECK(loop_header < loop_end); |
262 DCHECK(loop_stack_.top() < loop_header); | 392 DCHECK(loop_stack_.top().header_offset < loop_header); |
263 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); | 393 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); |
264 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); | 394 DCHECK(header_to_info_.find(loop_header) == header_to_info_.end()); |
265 | 395 |
266 end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header)); | 396 int parent_offset = loop_stack_.top().header_offset; |
267 header_to_parent_.insert( | 397 |
268 ZoneMap<int, int>::value_type(loop_header, loop_stack_.top())); | 398 end_to_header_.insert({loop_end, loop_header}); |
269 loop_stack_.push(loop_header); | 399 auto it = header_to_info_.insert( |
| 400 {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(), |
| 401 bytecode_array_->register_count(), zone_)}); |
| 402 // Get the loop info pointer from the output of insert. |
| 403 LoopInfo* loop_info = &it.first->second; |
| 404 |
| 405 loop_stack_.push({loop_header, loop_info}); |
270 } | 406 } |
271 | 407 |
272 bool BytecodeAnalysis::IsLoopHeader(int offset) const { | 408 bool BytecodeAnalysis::IsLoopHeader(int offset) const { |
273 return header_to_parent_.find(offset) != header_to_parent_.end(); | 409 return header_to_info_.find(offset) != header_to_info_.end(); |
274 } | 410 } |
275 | 411 |
276 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { | 412 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { |
277 auto loop_end_to_header = end_to_header_.upper_bound(offset); | 413 auto loop_end_to_header = end_to_header_.upper_bound(offset); |
278 // If there is no next end => offset is not in a loop. | 414 // If there is no next end => offset is not in a loop. |
279 if (loop_end_to_header == end_to_header_.end()) { | 415 if (loop_end_to_header == end_to_header_.end()) { |
280 return -1; | 416 return -1; |
281 } | 417 } |
282 // If the header preceeds the offset, this is the loop | 418 // If the header preceeds the offset, this is the loop |
283 // | 419 // |
284 // .> header <--loop_end_to_header | 420 // .> header <--loop_end_to_header |
285 // | | 421 // | |
286 // | <--offset | 422 // | <--offset |
287 // | | 423 // | |
288 // `- end | 424 // `- end |
289 if (loop_end_to_header->second <= offset) { | 425 if (loop_end_to_header->second <= offset) { |
290 return loop_end_to_header->second; | 426 return loop_end_to_header->second; |
291 } | 427 } |
292 // Otherwise there is a (potentially nested) loop after this offset. | 428 // Otherwise there is a (potentially nested) loop after this offset. |
293 // | 429 // |
294 // <--offset | 430 // <--offset |
295 // | 431 // |
296 // .> header | 432 // .> header |
297 // | | 433 // | |
298 // | .> header <--loop_end_to_header | 434 // | .> header <--loop_end_to_header |
299 // | | | 435 // | | |
300 // | `- end | 436 // | `- end |
301 // | | 437 // | |
302 // `- end | 438 // `- end |
303 // We just return the parent of the next loop header (might be -1). | 439 // We just return the parent of the next loop (might be -1). |
304 DCHECK(header_to_parent_.upper_bound(offset) != header_to_parent_.end()); | 440 DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end()); |
305 | 441 |
306 return header_to_parent_.upper_bound(offset)->second; | 442 return header_to_info_.upper_bound(offset)->second.parent_offset(); |
307 } | 443 } |
308 | 444 |
309 int BytecodeAnalysis::GetParentLoopFor(int header_offset) const { | 445 const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const { |
310 DCHECK(IsLoopHeader(header_offset)); | 446 DCHECK(IsLoopHeader(header_offset)); |
311 | 447 |
312 return header_to_parent_.find(header_offset)->second; | 448 return header_to_info_.find(header_offset)->second; |
313 } | 449 } |
314 | 450 |
315 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( | 451 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( |
316 int offset) const { | 452 int offset) const { |
317 if (!do_liveness_analysis_) return nullptr; | 453 if (!do_liveness_analysis_) return nullptr; |
318 | 454 |
319 return liveness_map_.GetInLiveness(offset); | 455 return liveness_map_.GetInLiveness(offset); |
320 } | 456 } |
321 | 457 |
322 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( | 458 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 } | 613 } |
478 } | 614 } |
479 | 615 |
480 return invalid_offset == -1; | 616 return invalid_offset == -1; |
481 } | 617 } |
482 #endif | 618 #endif |
483 | 619 |
484 } // namespace compiler | 620 } // namespace compiler |
485 } // namespace internal | 621 } // namespace internal |
486 } // namespace v8 | 622 } // namespace v8 |
OLD | NEW |