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