Chromium Code Reviews| 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 |