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 3 // found in the LICENSE file. 4 5 #include "src/compiler/bytecode-analysis.h" 6 7 #include "src/interpreter/bytecode-array-iterator.h" 8 #include "src/interpreter/bytecode-array-random-iterator.h" 9 #include "src/objects-inl.h" 10 11 namespace v8 { 12 namespace internal { 13 namespace compiler { 14 15 using namespace interpreter; 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 84 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, 85 Zone* zone, bool do_liveness_analysis) 86 : bytecode_array_(bytecode_array), 87 do_liveness_analysis_(do_liveness_analysis), 88 zone_(zone), 89 loop_stack_(zone), 90 loop_end_index_queue_(zone), 91 end_to_header_(zone), 92 header_to_info_(zone), 93 liveness_map_(bytecode_array->length(), zone) {} 94 95 namespace { 96 97 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness, 98 const BytecodeArrayAccessor& accessor) { 99 int num_operands = Bytecodes::NumberOfOperands(bytecode); 100 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); 101 102 if (Bytecodes::WritesAccumulator(bytecode)) { 103 in_liveness.MarkAccumulatorDead(); 104 } 105 for (int i = 0; i < num_operands; ++i) { 106 switch (operand_types[i]) { 107 case OperandType::kRegOut: { 108 interpreter::Register r = accessor.GetRegisterOperand(i); 109 if (!r.is_parameter()) { 110 in_liveness.MarkRegisterDead(r.index()); 111 } 112 break; 113 } 114 case OperandType::kRegOutPair: { 115 interpreter::Register r = accessor.GetRegisterOperand(i); 116 if (!r.is_parameter()) { 117 DCHECK(!interpreter::Register(r.index() + 1).is_parameter()); 118 in_liveness.MarkRegisterDead(r.index()); 119 in_liveness.MarkRegisterDead(r.index() + 1); 120 } 121 break; 122 } 123 case OperandType::kRegOutTriple: { 124 interpreter::Register r = accessor.GetRegisterOperand(i); 125 if (!r.is_parameter()) { 126 DCHECK(!interpreter::Register(r.index() + 1).is_parameter()); 127 DCHECK(!interpreter::Register(r.index() + 2).is_parameter()); 128 in_liveness.MarkRegisterDead(r.index()); 129 in_liveness.MarkRegisterDead(r.index() + 1); 130 in_liveness.MarkRegisterDead(r.index() + 2); 131 } 132 break; 133 } 134 default: 135 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i])); 136 break; 137 } 138 } 139 140 if (Bytecodes::ReadsAccumulator(bytecode)) { 141 in_liveness.MarkAccumulatorLive(); 142 } 143 for (int i = 0; i < num_operands; ++i) { 144 switch (operand_types[i]) { 145 case OperandType::kReg: { 146 interpreter::Register r = accessor.GetRegisterOperand(i); 147 if (!r.is_parameter()) { 148 in_liveness.MarkRegisterLive(r.index()); 149 } 150 break; 151 } 152 case OperandType::kRegPair: { 153 interpreter::Register r = accessor.GetRegisterOperand(i); 154 if (!r.is_parameter()) { 155 DCHECK(!interpreter::Register(r.index() + 1).is_parameter()); 156 in_liveness.MarkRegisterLive(r.index()); 157 in_liveness.MarkRegisterLive(r.index() + 1); 158 } 159 break; 160 } 161 case OperandType::kRegList: { 162 interpreter::Register r = accessor.GetRegisterOperand(i++); 163 uint32_t reg_count = accessor.GetRegisterCountOperand(i); 164 if (!r.is_parameter()) { 165 for (uint32_t j = 0; j < reg_count; ++j) { 166 DCHECK(!interpreter::Register(r.index() + j).is_parameter()); 167 in_liveness.MarkRegisterLive(r.index() + j); 168 } 169 } 170 } 171 default: 172 DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[i])); 173 break; 174 } 175 } 176 } 177 178 void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness, 179 BytecodeLivenessState* next_bytecode_in_liveness, 180 const BytecodeArrayAccessor& accessor, 181 const BytecodeLivenessMap& liveness_map) { 182 int current_offset = accessor.current_offset(); 183 const Handle<BytecodeArray>& bytecode_array = accessor.bytecode_array(); 184 185 // Update from jump target (if any). Skip loops, we update these manually in 186 // the liveness iterations. 187 if (Bytecodes::IsForwardJump(bytecode)) { 188 int target_offset = accessor.GetJumpTargetOffset(); 189 out_liveness.Union(*liveness_map.GetInLiveness(target_offset)); 190 } 191 192 // Update from next bytecode (unless there isn't one or this is an 193 // unconditional jump). 194 if (next_bytecode_in_liveness != nullptr && 195 !Bytecodes::IsUnconditionalJump(bytecode)) { 196 out_liveness.Union(*next_bytecode_in_liveness); 197 } 198 199 // Update from exception handler (if any). 200 if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) { 201 int handler_context; 202 // TODO(leszeks): We should look up this range only once per entry. 203 HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table()); 204 int handler_offset = 205 table->LookupRange(current_offset, &handler_context, nullptr); 206 207 if (handler_offset != -1) { 208 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset)); 209 out_liveness.MarkRegisterLive(handler_context); 210 } 211 } 212 } 213 214 void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments, 215 const BytecodeArrayAccessor& accessor) { 216 int num_operands = Bytecodes::NumberOfOperands(bytecode); 217 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); 218 219 for (int i = 0; i < num_operands; ++i) { 220 switch (operand_types[i]) { 221 case OperandType::kRegOut: { 222 assignments.Add(accessor.GetRegisterOperand(i)); 223 break; 224 } 225 case OperandType::kRegOutPair: { 226 assignments.AddPair(accessor.GetRegisterOperand(i)); 227 break; 228 } 229 case OperandType::kRegOutTriple: { 230 assignments.AddTriple(accessor.GetRegisterOperand(i)); 231 break; 232 } 233 default: 234 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i])); 235 break; 236 } 237 } 238 } 239 240 } // namespace 241 242 void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) { 243 loop_stack_.push({-1, nullptr}); 244 245 BytecodeLivenessState* next_bytecode_in_liveness = nullptr; 246 247 int osr_loop_end_offset = 248 osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt(); 249 250 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); 251 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { 252 Bytecode bytecode = iterator.current_bytecode(); 253 int current_offset = iterator.current_offset(); 254 255 if (bytecode == Bytecode::kJumpLoop) { 256 // Every byte up to and including the last byte within the backwards jump 257 // instruction is considered part of the loop, set loop end accordingly. 258 int loop_end = current_offset + iterator.current_bytecode_size(); 259 PushLoop(iterator.GetJumpTargetOffset(), loop_end); 260 261 // Normally prefixed bytecodes are treated as if the prefix's offset was 262 // the actual bytecode's offset. However, the OSR id is the offset of the 263 // actual JumpLoop bytecode, so we need to find the location of that 264 // bytecode ignoring the prefix. 265 int jump_loop_offset = current_offset + iterator.current_prefix_offset(); 266 bool is_osr_loop = (jump_loop_offset == osr_loop_end_offset); 267 268 // Check that is_osr_loop is set iff the osr_loop_end_offset is within 269 // this bytecode. 270 DCHECK(!is_osr_loop || 271 iterator.OffsetWithinBytecode(osr_loop_end_offset)); 272 273 // OSR "assigns" everything to OSR values on entry into an OSR loop, so we 274 // need to make sure to considered everything to be assigned. 275 if (is_osr_loop) { 276 loop_stack_.top().loop_info->assignments().AddAll(); 277 } 278 279 // Save the index so that we can do another pass later. 280 if (do_liveness_analysis_) { 281 loop_end_index_queue_.push_back(iterator.current_index()); 282 } 283 } else if (loop_stack_.size() > 1) { 284 LoopStackEntry& current_loop = loop_stack_.top(); 285 LoopInfo* current_loop_info = current_loop.loop_info; 286 287 // TODO(leszeks): Ideally, we'd only set values that were assigned in 288 // the loop *and* are live when the loop exits. However, this requires 289 // tracking the out-liveness of *all* loop exits, which is not 290 // information we currently have. 291 UpdateAssignments(bytecode, current_loop_info->assignments(), iterator); 292 293 if (current_offset == current_loop.header_offset) { 294 loop_stack_.pop(); 295 if (loop_stack_.size() > 1) { 296 // Propagate inner loop assignments to outer loop. 297 loop_stack_.top().loop_info->assignments().Union( 298 current_loop_info->assignments()); 299 } 300 } 301 } 302 303 if (do_liveness_analysis_) { 304 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( 305 current_offset, bytecode_array()->register_count(), zone()); 306 307 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, 308 iterator, liveness_map_); 309 liveness.in->CopyFrom(*liveness.out); 310 UpdateInLiveness(bytecode, *liveness.in, iterator); 311 312 next_bytecode_in_liveness = liveness.in; 313 } 314 } 315 316 DCHECK_EQ(loop_stack_.size(), 1u); 317 DCHECK_EQ(loop_stack_.top().header_offset, -1); 318 319 if (!do_liveness_analysis_) return; 320 321 // At this point, every bytecode has a valid in and out liveness, except for 322 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness 323 // analysis iterations can only add additional liveness bits that are pulled 324 // across these back edges. 325 // 326 // Furthermore, a loop header's in-liveness can only change based on any 327 // bytecodes *after* the loop end -- it cannot change as a result of the 328 // JumpLoop liveness being updated, as the only liveness bits than can be 329 // added to the loop body are those of the loop header. 330 // 331 // So, if we know that the liveness of bytecodes after a loop header won't 332 // change (e.g. because there are no loops in them, or we have already ensured 333 // those loops are valid), we can safely update the loop end and pass over the 334 // loop body, and then never have to pass over that loop end again, because we 335 // have shown that its target, the loop header, can't change from the entries 336 // after the loop, and can't change from any loop body pass. 337 // 338 // This means that in a pass, we can iterate backwards over the bytecode 339 // array, process any loops that we encounter, and on subsequent passes we can 340 // skip processing those loops (though we still have to process inner loops). 341 // 342 // Equivalently, we can queue up loop ends from back to front, and pass over 343 // the loops in that order, as this preserves both the bottom-to-top and 344 // outer-to-inner requirements. 345 346 for (int loop_end_index : loop_end_index_queue_) { 347 iterator.GoToIndex(loop_end_index); 348 349 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); 350 351 int header_offset = iterator.GetJumpTargetOffset(); 352 int end_offset = iterator.current_offset(); 353 354 BytecodeLiveness& header_liveness = 355 liveness_map_.GetLiveness(header_offset); 356 BytecodeLiveness& end_liveness = liveness_map_.GetLiveness(end_offset); 357 358 if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) { 359 // Only update the loop body if the loop end liveness changed. 360 continue; 361 } 362 end_liveness.in->CopyFrom(*end_liveness.out); 363 next_bytecode_in_liveness = end_liveness.in; 364 365 // Advance into the loop body. 366 --iterator; 367 for (; iterator.current_offset() > header_offset; --iterator) { 368 Bytecode bytecode = iterator.current_bytecode(); 369 370 int current_offset = iterator.current_offset(); 371 BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset); 372 373 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, 374 iterator, liveness_map_); 375 liveness.in->CopyFrom(*liveness.out); 376 UpdateInLiveness(bytecode, *liveness.in, iterator); 377 378 next_bytecode_in_liveness = liveness.in; 379 } 380 // Now we are at the loop header. Since the in-liveness of the header 381 // can't change, we need only to update the out-liveness. 382 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, 383 next_bytecode_in_liveness, iterator, liveness_map_); 384 } 385 386 DCHECK(LivenessIsValid()); 387 } 388 389 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { 390 DCHECK(loop_header < loop_end); 391 DCHECK(loop_stack_.top().header_offset < loop_header); 392 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); 393 DCHECK(header_to_info_.find(loop_header) == header_to_info_.end()); 394 395 int parent_offset = loop_stack_.top().header_offset; 396 397 end_to_header_.insert({loop_end, loop_header}); 398 auto it = header_to_info_.insert( 399 {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(), 400 bytecode_array_->register_count(), zone_)}); 401 // Get the loop info pointer from the output of insert. 402 LoopInfo* loop_info = &it.first->second; 403 404 loop_stack_.push({loop_header, loop_info}); 405 } 406 407 bool BytecodeAnalysis::IsLoopHeader(int offset) const { 408 return header_to_info_.find(offset) != header_to_info_.end(); 409 } 410 411 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { 412 auto loop_end_to_header = end_to_header_.upper_bound(offset); 413 // If there is no next end => offset is not in a loop. 414 if (loop_end_to_header == end_to_header_.end()) { 415 return -1; 416 } 417 // If the header preceeds the offset, this is the loop 418 // 419 // .> header <--loop_end_to_header 420 // | 421 // | <--offset 422 // | 423 // `- end 424 if (loop_end_to_header->second <= offset) { 425 return loop_end_to_header->second; 426 } 427 // Otherwise there is a (potentially nested) loop after this offset. 428 // 429 // <--offset 430 // 431 // .> header 432 // | 433 // | .> header <--loop_end_to_header 434 // | | 435 // | `- end 436 // | 437 // `- end 438 // We just return the parent of the next loop (might be -1). 439 DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end()); 440 441 return header_to_info_.upper_bound(offset)->second.parent_offset(); 442 } 443 444 const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const { 445 DCHECK(IsLoopHeader(header_offset)); 446 447 return header_to_info_.find(header_offset)->second; 448 } 449 450 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( 451 int offset) const { 452 if (!do_liveness_analysis_) return nullptr; 453 454 return liveness_map_.GetInLiveness(offset); 455 } 456 457 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( 458 int offset) const { 459 if (!do_liveness_analysis_) return nullptr; 460 461 return liveness_map_.GetOutLiveness(offset); 462 } 463 464 std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os) const { 465 interpreter::BytecodeArrayIterator iterator(bytecode_array()); 466 467 for (; !iterator.done(); iterator.Advance()) { 468 int current_offset = iterator.current_offset(); 469 470 const BitVector& in_liveness = 471 GetInLivenessFor(current_offset)->bit_vector(); 472 const BitVector& out_liveness = 473 GetOutLivenessFor(current_offset)->bit_vector(); 474 475 for (int i = 0; i < in_liveness.length(); ++i) { 476 os << (in_liveness.Contains(i) ? "L" : "."); 477 } 478 os << " -> "; 479 480 for (int i = 0; i < out_liveness.length(); ++i) { 481 os << (out_liveness.Contains(i) ? "L" : "."); 482 } 483 484 os << " | " << current_offset << ": "; 485 iterator.PrintTo(os) << std::endl; 486 } 487 488 return os; 489 } 490 491 #if DEBUG 492 bool BytecodeAnalysis::LivenessIsValid() { 493 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); 494 495 BytecodeLivenessState previous_liveness(bytecode_array()->register_count(), 496 zone()); 497 498 int invalid_offset = -1; 499 int which_invalid = -1; 500 501 BytecodeLivenessState* next_bytecode_in_liveness = nullptr; 502 503 // Ensure that there are no liveness changes if we iterate one more time. 504 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { 505 Bytecode bytecode = iterator.current_bytecode(); 506 507 int current_offset = iterator.current_offset(); 508 509 BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset); 510 511 previous_liveness.CopyFrom(*liveness.out); 512 513 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, 514 iterator, liveness_map_); 515 // UpdateOutLiveness skips kJumpLoop, so we update it manually. 516 if (bytecode == Bytecode::kJumpLoop) { 517 int target_offset = iterator.GetJumpTargetOffset(); 518 liveness.out->Union(*liveness_map_.GetInLiveness(target_offset)); 519 } 520 521 if (!liveness.out->Equals(previous_liveness)) { 522 // Reset the invalid liveness. 523 liveness.out->CopyFrom(previous_liveness); 524 invalid_offset = current_offset; 525 which_invalid = 1; 526 break; 527 } 528 529 previous_liveness.CopyFrom(*liveness.in); 530 531 liveness.in->CopyFrom(*liveness.out); 532 UpdateInLiveness(bytecode, *liveness.in, iterator); 533 534 if (!liveness.in->Equals(previous_liveness)) { 535 // Reset the invalid liveness. 536 liveness.in->CopyFrom(previous_liveness); 537 invalid_offset = current_offset; 538 which_invalid = 0; 539 break; 540 } 541 542 next_bytecode_in_liveness = liveness.in; 543 } 544 545 if (invalid_offset != -1) { 546 OFStream of(stderr); 547 of << "Invalid liveness:" << std::endl; 548 549 // Dump the bytecode, annotated with the liveness and marking loops. 550 551 int loop_indent = 0; 552 553 BytecodeArrayIterator forward_iterator(bytecode_array()); 554 for (; !forward_iterator.done(); forward_iterator.Advance()) { 555 int current_offset = forward_iterator.current_offset(); 556 const BitVector& in_liveness = 557 GetInLivenessFor(current_offset)->bit_vector(); 558 const BitVector& out_liveness = 559 GetOutLivenessFor(current_offset)->bit_vector(); 560 561 for (int i = 0; i < in_liveness.length(); ++i) { 562 of << (in_liveness.Contains(i) ? 'L' : '.'); 563 } 564 565 of << " | "; 566 567 for (int i = 0; i < out_liveness.length(); ++i) { 568 of << (out_liveness.Contains(i) ? 'L' : '.'); 569 } 570 571 of << " : " << current_offset << " : "; 572 573 // Draw loop back edges by indentin everything between loop headers and 574 // jump loop instructions. 575 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) { 576 loop_indent--; 577 } 578 for (int i = 0; i < loop_indent; ++i) { 579 of << " | "; 580 } 581 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) { 582 of << " `-" << current_offset; 583 } else if (IsLoopHeader(current_offset)) { 584 of << " .>" << current_offset; 585 loop_indent++; 586 } 587 forward_iterator.PrintTo(of) << std::endl; 588 589 if (current_offset == invalid_offset) { 590 // Underline the invalid liveness. 591 if (which_invalid == 0) { 592 for (int i = 0; i < in_liveness.length(); ++i) { 593 of << '^'; 594 } 595 } else { 596 for (int i = 0; i < in_liveness.length() + 3; ++i) { 597 of << ' '; 598 } 599 for (int i = 0; i < out_liveness.length(); ++i) { 600 of << '^'; 601 } 602 } 603 604 // Make sure to draw the loop indentation marks on this additional line. 605 of << " : " << current_offset << " : "; 606 for (int i = 0; i < loop_indent; ++i) { 607 of << " | "; 608 } 609 610 of << std::endl; 611 } 612 } 613 } 614 615 return invalid_offset == -1; 616 } 617 #endif 618 619 } // namespace compiler 620 } // namespace internal 621 } // namespace v8 622