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