1// Copyright 2014 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/backend/register-allocator-verifier.h" 6 7#include "src/compiler/backend/instruction.h" 8#include "src/utils/bit-vector.h" 9#include "src/utils/ostreams.h" 10 11namespace v8 { 12namespace internal { 13namespace compiler { 14 15namespace { 16 17size_t OperandCount(const Instruction* instr) { 18 return instr->InputCount() + instr->OutputCount() + instr->TempCount(); 19} 20 21void VerifyEmptyGaps(const Instruction* instr) { 22 for (int i = Instruction::FIRST_GAP_POSITION; 23 i <= Instruction::LAST_GAP_POSITION; i++) { 24 Instruction::GapPosition inner_pos = 25 static_cast<Instruction::GapPosition>(i); 26 CHECK_NULL(instr->GetParallelMove(inner_pos)); 27 } 28} 29 30void VerifyAllocatedGaps(const Instruction* instr, const char* caller_info) { 31 for (int i = Instruction::FIRST_GAP_POSITION; 32 i <= Instruction::LAST_GAP_POSITION; i++) { 33 Instruction::GapPosition inner_pos = 34 static_cast<Instruction::GapPosition>(i); 35 const ParallelMove* moves = instr->GetParallelMove(inner_pos); 36 if (moves == nullptr) continue; 37 for (const MoveOperands* move : *moves) { 38 if (move->IsRedundant()) continue; 39 CHECK_WITH_MSG( 40 move->source().IsAllocated() || move->source().IsConstant(), 41 caller_info); 42 CHECK_WITH_MSG(move->destination().IsAllocated(), caller_info); 43 } 44 } 45} 46 47int GetValue(const ImmediateOperand* imm) { 48 switch (imm->type()) { 49 case ImmediateOperand::INLINE_INT32: 50 return imm->inline_int32_value(); 51 case ImmediateOperand::INLINE_INT64: 52 return static_cast<int>(imm->inline_int64_value()); 53 case ImmediateOperand::INDEXED_RPO: 54 case ImmediateOperand::INDEXED_IMM: 55 return imm->indexed_value(); 56 } 57} 58 59} // namespace 60 61RegisterAllocatorVerifier::RegisterAllocatorVerifier( 62 Zone* zone, const RegisterConfiguration* config, 63 const InstructionSequence* sequence, const Frame* frame) 64 : zone_(zone), 65 config_(config), 66 sequence_(sequence), 67 constraints_(zone), 68 assessments_(zone), 69 outstanding_assessments_(zone), 70 spill_slot_delta_(frame->GetTotalFrameSlotCount() - 71 frame->GetSpillSlotCount()) { 72 constraints_.reserve(sequence->instructions().size()); 73 // TODO(dcarney): model unique constraints. 74 // Construct OperandConstraints for all InstructionOperands, eliminating 75 // kSameAsInput along the way. 76 for (const Instruction* instr : sequence->instructions()) { 77 // All gaps should be totally unallocated at this point. 78 VerifyEmptyGaps(instr); 79 const size_t operand_count = OperandCount(instr); 80 OperandConstraint* op_constraints = 81 zone->NewArray<OperandConstraint>(operand_count); 82 size_t count = 0; 83 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { 84 BuildConstraint(instr->InputAt(i), &op_constraints[count]); 85 VerifyInput(op_constraints[count]); 86 } 87 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { 88 BuildConstraint(instr->TempAt(i), &op_constraints[count]); 89 VerifyTemp(op_constraints[count]); 90 } 91 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { 92 BuildConstraint(instr->OutputAt(i), &op_constraints[count]); 93 if (op_constraints[count].type_ == kSameAsInput) { 94 int input_index = op_constraints[count].value_; 95 CHECK_LT(input_index, instr->InputCount()); 96 op_constraints[count].type_ = op_constraints[input_index].type_; 97 op_constraints[count].value_ = op_constraints[input_index].value_; 98 } 99 VerifyOutput(op_constraints[count]); 100 } 101 InstructionConstraint instr_constraint = {instr, operand_count, 102 op_constraints}; 103 constraints()->push_back(instr_constraint); 104 } 105} 106 107void RegisterAllocatorVerifier::VerifyInput( 108 const OperandConstraint& constraint) { 109 CHECK_NE(kSameAsInput, constraint.type_); 110 if (constraint.type_ != kImmediate) { 111 CHECK_NE(InstructionOperand::kInvalidVirtualRegister, 112 constraint.virtual_register_); 113 } 114} 115 116void RegisterAllocatorVerifier::VerifyTemp( 117 const OperandConstraint& constraint) { 118 CHECK_NE(kSameAsInput, constraint.type_); 119 CHECK_NE(kImmediate, constraint.type_); 120 CHECK_NE(kConstant, constraint.type_); 121} 122 123void RegisterAllocatorVerifier::VerifyOutput( 124 const OperandConstraint& constraint) { 125 CHECK_NE(kImmediate, constraint.type_); 126 CHECK_NE(InstructionOperand::kInvalidVirtualRegister, 127 constraint.virtual_register_); 128} 129 130void RegisterAllocatorVerifier::VerifyAssignment(const char* caller_info) { 131 caller_info_ = caller_info; 132 CHECK(sequence()->instructions().size() == constraints()->size()); 133 auto instr_it = sequence()->begin(); 134 for (const auto& instr_constraint : *constraints()) { 135 const Instruction* instr = instr_constraint.instruction_; 136 // All gaps should be totally allocated at this point. 137 VerifyAllocatedGaps(instr, caller_info_); 138 const size_t operand_count = instr_constraint.operand_constaints_size_; 139 const OperandConstraint* op_constraints = 140 instr_constraint.operand_constraints_; 141 CHECK_EQ(instr, *instr_it); 142 CHECK(operand_count == OperandCount(instr)); 143 size_t count = 0; 144 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { 145 CheckConstraint(instr->InputAt(i), &op_constraints[count]); 146 } 147 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { 148 CheckConstraint(instr->TempAt(i), &op_constraints[count]); 149 } 150 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { 151 CheckConstraint(instr->OutputAt(i), &op_constraints[count]); 152 } 153 ++instr_it; 154 } 155} 156 157void RegisterAllocatorVerifier::BuildConstraint(const InstructionOperand* op, 158 OperandConstraint* constraint) { 159 constraint->value_ = kMinInt; 160 constraint->virtual_register_ = InstructionOperand::kInvalidVirtualRegister; 161 if (op->IsConstant()) { 162 constraint->type_ = kConstant; 163 constraint->value_ = ConstantOperand::cast(op)->virtual_register(); 164 constraint->virtual_register_ = constraint->value_; 165 } else if (op->IsImmediate()) { 166 const ImmediateOperand* imm = ImmediateOperand::cast(op); 167 constraint->type_ = kImmediate; 168 constraint->value_ = GetValue(imm); 169 } else { 170 CHECK(op->IsUnallocated()); 171 const UnallocatedOperand* unallocated = UnallocatedOperand::cast(op); 172 int vreg = unallocated->virtual_register(); 173 constraint->virtual_register_ = vreg; 174 if (unallocated->basic_policy() == UnallocatedOperand::FIXED_SLOT) { 175 constraint->type_ = kFixedSlot; 176 constraint->value_ = unallocated->fixed_slot_index(); 177 } else { 178 switch (unallocated->extended_policy()) { 179 case UnallocatedOperand::REGISTER_OR_SLOT: 180 case UnallocatedOperand::NONE: 181 if (sequence()->IsFP(vreg)) { 182 constraint->type_ = kRegisterOrSlotFP; 183 } else { 184 constraint->type_ = kRegisterOrSlot; 185 } 186 break; 187 case UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT: 188 DCHECK(!sequence()->IsFP(vreg)); 189 constraint->type_ = kRegisterOrSlotOrConstant; 190 break; 191 case UnallocatedOperand::FIXED_REGISTER: 192 if (unallocated->HasSecondaryStorage()) { 193 constraint->type_ = kRegisterAndSlot; 194 constraint->spilled_slot_ = unallocated->GetSecondaryStorage(); 195 } else { 196 constraint->type_ = kFixedRegister; 197 } 198 constraint->value_ = unallocated->fixed_register_index(); 199 break; 200 case UnallocatedOperand::FIXED_FP_REGISTER: 201 constraint->type_ = kFixedFPRegister; 202 constraint->value_ = unallocated->fixed_register_index(); 203 break; 204 case UnallocatedOperand::MUST_HAVE_REGISTER: 205 if (sequence()->IsFP(vreg)) { 206 constraint->type_ = kFPRegister; 207 } else { 208 constraint->type_ = kRegister; 209 } 210 break; 211 case UnallocatedOperand::MUST_HAVE_SLOT: 212 constraint->type_ = kSlot; 213 constraint->value_ = 214 ElementSizeLog2Of(sequence()->GetRepresentation(vreg)); 215 break; 216 case UnallocatedOperand::SAME_AS_INPUT: 217 constraint->type_ = kSameAsInput; 218 constraint->value_ = unallocated->input_index(); 219 break; 220 } 221 } 222 } 223} 224 225void RegisterAllocatorVerifier::CheckConstraint( 226 const InstructionOperand* op, const OperandConstraint* constraint) { 227 switch (constraint->type_) { 228 case kConstant: 229 CHECK_WITH_MSG(op->IsConstant(), caller_info_); 230 CHECK_EQ(ConstantOperand::cast(op)->virtual_register(), 231 constraint->value_); 232 return; 233 case kImmediate: { 234 CHECK_WITH_MSG(op->IsImmediate(), caller_info_); 235 const ImmediateOperand* imm = ImmediateOperand::cast(op); 236 int value = GetValue(imm); 237 CHECK_EQ(value, constraint->value_); 238 return; 239 } 240 case kRegister: 241 CHECK_WITH_MSG(op->IsRegister(), caller_info_); 242 return; 243 case kFPRegister: 244 CHECK_WITH_MSG(op->IsFPRegister(), caller_info_); 245 return; 246 case kFixedRegister: 247 case kRegisterAndSlot: 248 CHECK_WITH_MSG(op->IsRegister(), caller_info_); 249 CHECK_EQ(LocationOperand::cast(op)->register_code(), constraint->value_); 250 return; 251 case kFixedFPRegister: 252 CHECK_WITH_MSG(op->IsFPRegister(), caller_info_); 253 CHECK_EQ(LocationOperand::cast(op)->register_code(), constraint->value_); 254 return; 255 case kFixedSlot: 256 CHECK_WITH_MSG(op->IsStackSlot() || op->IsFPStackSlot(), caller_info_); 257 CHECK_EQ(LocationOperand::cast(op)->index(), constraint->value_); 258 return; 259 case kSlot: 260 CHECK_WITH_MSG(op->IsStackSlot() || op->IsFPStackSlot(), caller_info_); 261 CHECK_EQ(ElementSizeLog2Of(LocationOperand::cast(op)->representation()), 262 constraint->value_); 263 return; 264 case kRegisterOrSlot: 265 CHECK_WITH_MSG(op->IsRegister() || op->IsStackSlot(), caller_info_); 266 return; 267 case kRegisterOrSlotFP: 268 CHECK_WITH_MSG(op->IsFPRegister() || op->IsFPStackSlot(), caller_info_); 269 return; 270 case kRegisterOrSlotOrConstant: 271 CHECK_WITH_MSG(op->IsRegister() || op->IsStackSlot() || op->IsConstant(), 272 caller_info_); 273 return; 274 case kSameAsInput: 275 CHECK_WITH_MSG(false, caller_info_); 276 return; 277 } 278} 279 280void BlockAssessments::PerformMoves(const Instruction* instruction) { 281 const ParallelMove* first = 282 instruction->GetParallelMove(Instruction::GapPosition::START); 283 PerformParallelMoves(first); 284 const ParallelMove* last = 285 instruction->GetParallelMove(Instruction::GapPosition::END); 286 PerformParallelMoves(last); 287} 288 289void BlockAssessments::PerformParallelMoves(const ParallelMove* moves) { 290 if (moves == nullptr) return; 291 292 CHECK(map_for_moves_.empty()); 293 for (MoveOperands* move : *moves) { 294 if (move->IsEliminated() || move->IsRedundant()) continue; 295 auto it = map_.find(move->source()); 296 // The RHS of a parallel move should have been already assessed. 297 CHECK(it != map_.end()); 298 // The LHS of a parallel move should not have been assigned in this 299 // parallel move. 300 CHECK(map_for_moves_.find(move->destination()) == map_for_moves_.end()); 301 // The RHS of a parallel move should not be a stale reference. 302 CHECK(!IsStaleReferenceStackSlot(move->source())); 303 // Copy the assessment to the destination. 304 map_for_moves_[move->destination()] = it->second; 305 } 306 for (auto pair : map_for_moves_) { 307 // Re-insert the existing key for the new assignment so that it has the 308 // correct representation (which is ignored by the canonicalizing map 309 // comparator). 310 InstructionOperand op = pair.first; 311 map_.erase(op); 312 map_.insert(pair); 313 // Destination is no longer a stale reference. 314 stale_ref_stack_slots().erase(op); 315 } 316 map_for_moves_.clear(); 317} 318 319void BlockAssessments::DropRegisters() { 320 for (auto iterator = map().begin(), end = map().end(); iterator != end;) { 321 auto current = iterator; 322 ++iterator; 323 InstructionOperand op = current->first; 324 if (op.IsAnyRegister()) map().erase(current); 325 } 326} 327 328void BlockAssessments::CheckReferenceMap(const ReferenceMap* reference_map) { 329 // First mark all existing reference stack spill slots as stale. 330 for (auto pair : map()) { 331 InstructionOperand op = pair.first; 332 if (op.IsStackSlot()) { 333 const LocationOperand* loc_op = LocationOperand::cast(&op); 334 // Only mark arguments that are spill slots as stale, the reference map 335 // doesn't track arguments or fixed stack slots, which are implicitly 336 // tracked by the GC. 337 if (CanBeTaggedOrCompressedPointer(loc_op->representation()) && 338 loc_op->index() >= spill_slot_delta()) { 339 stale_ref_stack_slots().insert(op); 340 } 341 } 342 } 343 344 // Now remove any stack spill slots in the reference map from the list of 345 // stale slots. 346 for (auto ref_map_operand : reference_map->reference_operands()) { 347 if (ref_map_operand.IsStackSlot()) { 348 auto pair = map().find(ref_map_operand); 349 CHECK(pair != map().end()); 350 stale_ref_stack_slots().erase(pair->first); 351 } 352 } 353} 354 355bool BlockAssessments::IsStaleReferenceStackSlot(InstructionOperand op) { 356 if (!op.IsStackSlot()) return false; 357 358 const LocationOperand* loc_op = LocationOperand::cast(&op); 359 return CanBeTaggedOrCompressedPointer(loc_op->representation()) && 360 stale_ref_stack_slots().find(op) != stale_ref_stack_slots().end(); 361} 362 363void BlockAssessments::Print() const { 364 StdoutStream os; 365 for (const auto& pair : map()) { 366 const InstructionOperand op = pair.first; 367 const Assessment* assessment = pair.second; 368 // Use operator<< so we can write the assessment on the same 369 // line. 370 os << op << " : "; 371 if (assessment->kind() == AssessmentKind::Final) { 372 os << "v" << FinalAssessment::cast(assessment)->virtual_register(); 373 } else { 374 os << "P"; 375 } 376 if (stale_ref_stack_slots().find(op) != stale_ref_stack_slots().end()) { 377 os << " (stale reference)"; 378 } 379 os << std::endl; 380 } 381 os << std::endl; 382} 383 384BlockAssessments* RegisterAllocatorVerifier::CreateForBlock( 385 const InstructionBlock* block) { 386 RpoNumber current_block_id = block->rpo_number(); 387 388 BlockAssessments* ret = 389 zone()->New<BlockAssessments>(zone(), spill_slot_delta()); 390 if (block->PredecessorCount() == 0) { 391 // TODO(mtrofin): the following check should hold, however, in certain 392 // unit tests it is invalidated by the last block. Investigate and 393 // normalize the CFG. 394 // CHECK_EQ(0, current_block_id.ToInt()); 395 // The phi size test below is because we can, technically, have phi 396 // instructions with one argument. Some tests expose that, too. 397 } else if (block->PredecessorCount() == 1 && block->phis().size() == 0) { 398 const BlockAssessments* prev_block = assessments_[block->predecessors()[0]]; 399 ret->CopyFrom(prev_block); 400 } else { 401 for (RpoNumber pred_id : block->predecessors()) { 402 // For every operand coming from any of the predecessors, create an 403 // Unfinalized assessment. 404 auto iterator = assessments_.find(pred_id); 405 if (iterator == assessments_.end()) { 406 // This block is the head of a loop, and this predecessor is the 407 // loopback 408 // arc. 409 // Validate this is a loop case, otherwise the CFG is malformed. 410 CHECK(pred_id >= current_block_id); 411 CHECK(block->IsLoopHeader()); 412 continue; 413 } 414 const BlockAssessments* pred_assessments = iterator->second; 415 CHECK_NOT_NULL(pred_assessments); 416 for (auto pair : pred_assessments->map()) { 417 InstructionOperand operand = pair.first; 418 if (ret->map().find(operand) == ret->map().end()) { 419 ret->map().insert(std::make_pair( 420 operand, zone()->New<PendingAssessment>(zone(), block, operand))); 421 } 422 } 423 424 // Any references stack slots that became stale in predecessors will be 425 // stale here. 426 ret->stale_ref_stack_slots().insert( 427 pred_assessments->stale_ref_stack_slots().begin(), 428 pred_assessments->stale_ref_stack_slots().end()); 429 } 430 } 431 return ret; 432} 433 434void RegisterAllocatorVerifier::ValidatePendingAssessment( 435 RpoNumber block_id, InstructionOperand op, 436 const BlockAssessments* current_assessments, 437 PendingAssessment* const assessment, int virtual_register) { 438 if (assessment->IsAliasOf(virtual_register)) return; 439 440 // When validating a pending assessment, it is possible some of the 441 // assessments for the original operand (the one where the assessment was 442 // created for first) are also pending. To avoid recursion, we use a work 443 // list. To deal with cycles, we keep a set of seen nodes. 444 Zone local_zone(zone()->allocator(), ZONE_NAME); 445 ZoneQueue<std::pair<const PendingAssessment*, int>> worklist(&local_zone); 446 ZoneSet<RpoNumber> seen(&local_zone); 447 worklist.push(std::make_pair(assessment, virtual_register)); 448 seen.insert(block_id); 449 450 while (!worklist.empty()) { 451 auto work = worklist.front(); 452 const PendingAssessment* current_assessment = work.first; 453 int current_virtual_register = work.second; 454 InstructionOperand current_operand = current_assessment->operand(); 455 worklist.pop(); 456 457 const InstructionBlock* origin = current_assessment->origin(); 458 CHECK(origin->PredecessorCount() > 1 || origin->phis().size() > 0); 459 460 // Check if the virtual register is a phi first, instead of relying on 461 // the incoming assessments. In particular, this handles the case 462 // v1 = phi v0 v0, which structurally is identical to v0 having been 463 // defined at the top of a diamond, and arriving at the node joining the 464 // diamond's branches. 465 const PhiInstruction* phi = nullptr; 466 for (const PhiInstruction* candidate : origin->phis()) { 467 if (candidate->virtual_register() == current_virtual_register) { 468 phi = candidate; 469 break; 470 } 471 } 472 473 int op_index = 0; 474 for (RpoNumber pred : origin->predecessors()) { 475 int expected = 476 phi != nullptr ? phi->operands()[op_index] : current_virtual_register; 477 478 ++op_index; 479 auto pred_assignment = assessments_.find(pred); 480 if (pred_assignment == assessments_.end()) { 481 CHECK(origin->IsLoopHeader()); 482 auto todo_iter = outstanding_assessments_.find(pred); 483 DelayedAssessments* set = nullptr; 484 if (todo_iter == outstanding_assessments_.end()) { 485 set = zone()->New<DelayedAssessments>(zone()); 486 outstanding_assessments_.insert(std::make_pair(pred, set)); 487 } else { 488 set = todo_iter->second; 489 } 490 set->AddDelayedAssessment(current_operand, expected); 491 continue; 492 } 493 494 const BlockAssessments* pred_assessments = pred_assignment->second; 495 auto found_contribution = pred_assessments->map().find(current_operand); 496 CHECK(found_contribution != pred_assessments->map().end()); 497 Assessment* contribution = found_contribution->second; 498 499 switch (contribution->kind()) { 500 case Final: 501 CHECK_EQ(FinalAssessment::cast(contribution)->virtual_register(), 502 expected); 503 break; 504 case Pending: { 505 // This happens if we have a diamond feeding into another one, and 506 // the inner one never being used - other than for carrying the value. 507 const PendingAssessment* next = PendingAssessment::cast(contribution); 508 if (seen.find(pred) == seen.end()) { 509 worklist.push({next, expected}); 510 seen.insert(pred); 511 } 512 // Note that we do not want to finalize pending assessments at the 513 // beginning of a block - which is the information we'd have 514 // available here. This is because this operand may be reused to 515 // define duplicate phis. 516 break; 517 } 518 } 519 } 520 } 521 assessment->AddAlias(virtual_register); 522} 523 524void RegisterAllocatorVerifier::ValidateUse( 525 RpoNumber block_id, BlockAssessments* current_assessments, 526 InstructionOperand op, int virtual_register) { 527 auto iterator = current_assessments->map().find(op); 528 // We should have seen this operand before. 529 CHECK(iterator != current_assessments->map().end()); 530 Assessment* assessment = iterator->second; 531 532 // The operand shouldn't be a stale reference stack slot. 533 CHECK(!current_assessments->IsStaleReferenceStackSlot(op)); 534 535 switch (assessment->kind()) { 536 case Final: 537 CHECK_EQ(FinalAssessment::cast(assessment)->virtual_register(), 538 virtual_register); 539 break; 540 case Pending: { 541 PendingAssessment* pending = PendingAssessment::cast(assessment); 542 ValidatePendingAssessment(block_id, op, current_assessments, pending, 543 virtual_register); 544 break; 545 } 546 } 547} 548 549void RegisterAllocatorVerifier::VerifyGapMoves() { 550 CHECK(assessments_.empty()); 551 CHECK(outstanding_assessments_.empty()); 552 const size_t block_count = sequence()->instruction_blocks().size(); 553 for (size_t block_index = 0; block_index < block_count; ++block_index) { 554 const InstructionBlock* block = 555 sequence()->instruction_blocks()[block_index]; 556 BlockAssessments* block_assessments = CreateForBlock(block); 557 558 for (int instr_index = block->code_start(); instr_index < block->code_end(); 559 ++instr_index) { 560 const InstructionConstraint& instr_constraint = constraints_[instr_index]; 561 const Instruction* instr = instr_constraint.instruction_; 562 block_assessments->PerformMoves(instr); 563 564 const OperandConstraint* op_constraints = 565 instr_constraint.operand_constraints_; 566 size_t count = 0; 567 for (size_t i = 0; i < instr->InputCount(); ++i, ++count) { 568 if (op_constraints[count].type_ == kImmediate) { 569 continue; 570 } 571 int virtual_register = op_constraints[count].virtual_register_; 572 InstructionOperand op = *instr->InputAt(i); 573 ValidateUse(block->rpo_number(), block_assessments, op, 574 virtual_register); 575 } 576 for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { 577 block_assessments->Drop(*instr->TempAt(i)); 578 } 579 if (instr->IsCall()) { 580 block_assessments->DropRegisters(); 581 } 582 if (instr->HasReferenceMap()) { 583 block_assessments->CheckReferenceMap(instr->reference_map()); 584 } 585 for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { 586 int virtual_register = op_constraints[count].virtual_register_; 587 block_assessments->AddDefinition(*instr->OutputAt(i), virtual_register); 588 if (op_constraints[count].type_ == kRegisterAndSlot) { 589 const AllocatedOperand* reg_op = 590 AllocatedOperand::cast(instr->OutputAt(i)); 591 MachineRepresentation rep = reg_op->representation(); 592 const AllocatedOperand* stack_op = AllocatedOperand::New( 593 zone(), LocationOperand::LocationKind::STACK_SLOT, rep, 594 op_constraints[i].spilled_slot_); 595 block_assessments->AddDefinition(*stack_op, virtual_register); 596 } 597 } 598 } 599 // Now commit the assessments for this block. If there are any delayed 600 // assessments, ValidatePendingAssessment should see this block, too. 601 assessments_[block->rpo_number()] = block_assessments; 602 603 auto todo_iter = outstanding_assessments_.find(block->rpo_number()); 604 if (todo_iter == outstanding_assessments_.end()) continue; 605 DelayedAssessments* todo = todo_iter->second; 606 for (auto pair : todo->map()) { 607 InstructionOperand op = pair.first; 608 int vreg = pair.second; 609 auto found_op = block_assessments->map().find(op); 610 CHECK(found_op != block_assessments->map().end()); 611 // This block is a jump back to the loop header, ensure that the op hasn't 612 // become a stale reference during the blocks in the loop. 613 CHECK(!block_assessments->IsStaleReferenceStackSlot(op)); 614 switch (found_op->second->kind()) { 615 case Final: 616 CHECK_EQ(FinalAssessment::cast(found_op->second)->virtual_register(), 617 vreg); 618 break; 619 case Pending: 620 ValidatePendingAssessment(block->rpo_number(), op, block_assessments, 621 PendingAssessment::cast(found_op->second), 622 vreg); 623 break; 624 } 625 } 626 } 627} 628 629} // namespace compiler 630} // namespace internal 631} // namespace v8 632