Index: src/compiler/register-allocator-verifier.cc |
diff --git a/src/compiler/register-allocator-verifier.cc b/src/compiler/register-allocator-verifier.cc |
index f2160f52ce7a788530be617526b25f5a7d286740..eddcb3657a6a373ec56067f68aa4482b29d5ea6a 100644 |
--- a/src/compiler/register-allocator-verifier.cc |
+++ b/src/compiler/register-allocator-verifier.cc |
@@ -44,39 +44,17 @@ void VerifyAllocatedGaps(const Instruction* instr) { |
} // namespace |
- |
-void RegisterAllocatorVerifier::VerifyInput( |
- const OperandConstraint& constraint) { |
- CHECK_NE(kSameAsFirst, constraint.type_); |
- if (constraint.type_ != kImmediate && constraint.type_ != kExplicit) { |
- CHECK_NE(InstructionOperand::kInvalidVirtualRegister, |
- constraint.virtual_register_); |
- } |
-} |
- |
- |
-void RegisterAllocatorVerifier::VerifyTemp( |
- const OperandConstraint& constraint) { |
- CHECK_NE(kSameAsFirst, constraint.type_); |
- CHECK_NE(kImmediate, constraint.type_); |
- CHECK_NE(kExplicit, constraint.type_); |
- CHECK_NE(kConstant, constraint.type_); |
-} |
- |
- |
-void RegisterAllocatorVerifier::VerifyOutput( |
- const OperandConstraint& constraint) { |
- CHECK_NE(kImmediate, constraint.type_); |
- CHECK_NE(kExplicit, constraint.type_); |
- CHECK_NE(InstructionOperand::kInvalidVirtualRegister, |
- constraint.virtual_register_); |
-} |
- |
- |
RegisterAllocatorVerifier::RegisterAllocatorVerifier( |
Zone* zone, const RegisterConfiguration* config, |
const InstructionSequence* sequence) |
- : zone_(zone), config_(config), sequence_(sequence), constraints_(zone) { |
+ : zone_(zone), |
+ config_(config), |
+ sequence_(sequence), |
+ constraints_(zone), |
+ assessments_(zone), |
+ outstanding_assessments_(zone), |
+ worklist_(zone), |
+ seen_(zone) { |
constraints_.reserve(sequence->instructions().size()); |
// TODO(dcarney): model unique constraints. |
// Construct OperandConstraints for all InstructionOperands, eliminating |
@@ -111,6 +89,30 @@ RegisterAllocatorVerifier::RegisterAllocatorVerifier( |
} |
} |
+void RegisterAllocatorVerifier::VerifyInput( |
+ const OperandConstraint& constraint) { |
+ CHECK_NE(kSameAsFirst, constraint.type_); |
+ if (constraint.type_ != kImmediate && constraint.type_ != kExplicit) { |
+ CHECK_NE(InstructionOperand::kInvalidVirtualRegister, |
+ constraint.virtual_register_); |
+ } |
+} |
+ |
+void RegisterAllocatorVerifier::VerifyTemp( |
+ const OperandConstraint& constraint) { |
+ CHECK_NE(kSameAsFirst, constraint.type_); |
+ CHECK_NE(kImmediate, constraint.type_); |
+ CHECK_NE(kExplicit, constraint.type_); |
+ CHECK_NE(kConstant, constraint.type_); |
+} |
+ |
+void RegisterAllocatorVerifier::VerifyOutput( |
+ const OperandConstraint& constraint) { |
+ CHECK_NE(kImmediate, constraint.type_); |
+ CHECK_NE(kExplicit, constraint.type_); |
+ CHECK_NE(InstructionOperand::kInvalidVirtualRegister, |
+ constraint.virtual_register_); |
+} |
void RegisterAllocatorVerifier::VerifyAssignment() { |
CHECK(sequence()->instructions().size() == constraints()->size()); |
@@ -138,7 +140,6 @@ void RegisterAllocatorVerifier::VerifyAssignment() { |
} |
} |
- |
void RegisterAllocatorVerifier::BuildConstraint(const InstructionOperand* op, |
OperandConstraint* constraint) { |
constraint->value_ = kMinInt; |
@@ -204,7 +205,6 @@ void RegisterAllocatorVerifier::BuildConstraint(const InstructionOperand* op, |
} |
} |
- |
void RegisterAllocatorVerifier::CheckConstraint( |
const InstructionOperand* op, const OperandConstraint* constraint) { |
switch (constraint->type_) { |
@@ -264,457 +264,217 @@ void RegisterAllocatorVerifier::CheckConstraint( |
} |
} |
-namespace { |
- |
-typedef RpoNumber Rpo; |
- |
-static const int kInvalidVreg = InstructionOperand::kInvalidVirtualRegister; |
- |
-struct PhiData : public ZoneObject { |
- PhiData(Rpo definition_rpo, const PhiInstruction* phi, int first_pred_vreg, |
- const PhiData* first_pred_phi, Zone* zone) |
- : definition_rpo(definition_rpo), |
- virtual_register(phi->virtual_register()), |
- first_pred_vreg(first_pred_vreg), |
- first_pred_phi(first_pred_phi), |
- operands(zone) { |
- operands.reserve(phi->operands().size()); |
- operands.insert(operands.begin(), phi->operands().begin(), |
- phi->operands().end()); |
- } |
- const Rpo definition_rpo; |
- const int virtual_register; |
- const int first_pred_vreg; |
- const PhiData* first_pred_phi; |
- IntVector operands; |
-}; |
- |
-class PhiMap : public ZoneMap<int, PhiData*>, public ZoneObject { |
- public: |
- explicit PhiMap(Zone* zone) : ZoneMap<int, PhiData*>(zone) {} |
-}; |
- |
-struct OperandLess { |
- bool operator()(const InstructionOperand* a, |
- const InstructionOperand* b) const { |
- return a->CompareCanonicalized(*b); |
- } |
-}; |
- |
-class OperandMap : public ZoneObject { |
- public: |
- struct MapValue : public ZoneObject { |
- MapValue() |
- : incoming(nullptr), |
- define_vreg(kInvalidVreg), |
- use_vreg(kInvalidVreg), |
- succ_vreg(kInvalidVreg) {} |
- MapValue* incoming; // value from first predecessor block. |
- int define_vreg; // valid if this value was defined in this block. |
- int use_vreg; // valid if this value was used in this block. |
- int succ_vreg; // valid if propagated back from successor block. |
- }; |
- |
- class Map |
- : public ZoneMap<const InstructionOperand*, MapValue*, OperandLess> { |
- public: |
- explicit Map(Zone* zone) |
- : ZoneMap<const InstructionOperand*, MapValue*, OperandLess>(zone) {} |
- |
- // Remove all entries with keys not in other. |
- void Intersect(const Map& other) { |
- if (this->empty()) return; |
- auto it = this->begin(); |
- OperandLess less; |
- for (const std::pair<const InstructionOperand*, MapValue*>& o : other) { |
- while (less(it->first, o.first)) { |
- this->erase(it++); |
- if (it == this->end()) return; |
- } |
- if (it->first->EqualsCanonicalized(*o.first)) { |
- ++it; |
- if (it == this->end()) return; |
- } else { |
- CHECK(less(o.first, it->first)); |
- } |
- } |
- } |
- }; |
- |
- explicit OperandMap(Zone* zone) : map_(zone) {} |
- |
- Map& map() { return map_; } |
+void BlockAssessments::PerformMoves(const Instruction* instruction) { |
+ const ParallelMove* first = |
+ instruction->GetParallelMove(Instruction::GapPosition::START); |
+ PerformParallelMoves(first); |
+ const ParallelMove* last = |
+ instruction->GetParallelMove(Instruction::GapPosition::END); |
+ PerformParallelMoves(last); |
+} |
- void RunParallelMoves(Zone* zone, const ParallelMove* moves) { |
- // Compute outgoing mappings. |
- Map to_insert(zone); |
- for (const MoveOperands* move : *moves) { |
- if (move->IsEliminated()) continue; |
- auto cur = map().find(&move->source()); |
- CHECK(cur != map().end()); |
- auto res = |
- to_insert.insert(std::make_pair(&move->destination(), cur->second)); |
- // Ensure injectivity of moves. |
- CHECK(res.second); |
- } |
- // Drop current mappings. |
- for (const MoveOperands* move : *moves) { |
- if (move->IsEliminated()) continue; |
- auto cur = map().find(&move->destination()); |
- if (cur != map().end()) map().erase(cur); |
- } |
- // Insert new values. |
- map().insert(to_insert.begin(), to_insert.end()); |
+void BlockAssessments::PerformParallelMoves(const ParallelMove* moves) { |
+ if (moves == nullptr) return; |
+ |
+ CHECK(map_for_moves_.empty()); |
+ for (MoveOperands* move : *moves) { |
+ if (move->IsEliminated() || move->IsRedundant()) continue; |
+ auto it = map_.find(move->source()); |
+ // The RHS of a parallel move should have been already assessed. |
+ CHECK(it != map_.end()); |
+ // The LHS of a parallel move should not have been assigned in this |
+ // parallel move. |
+ CHECK(map_for_moves_.find(move->destination()) == map_for_moves_.end()); |
+ // Copy the assessment to the destination. |
+ map_for_moves_[move->destination()] = it->second; |
} |
- |
- void RunGaps(Zone* zone, const Instruction* instr) { |
- for (int i = Instruction::FIRST_GAP_POSITION; |
- i <= Instruction::LAST_GAP_POSITION; i++) { |
- Instruction::GapPosition inner_pos = |
- static_cast<Instruction::GapPosition>(i); |
- const ParallelMove* move = instr->GetParallelMove(inner_pos); |
- if (move == nullptr) continue; |
- RunParallelMoves(zone, move); |
- } |
+ for (auto pair : map_for_moves_) { |
+ map_[pair.first] = pair.second; |
} |
+ map_for_moves_.clear(); |
+} |
- void Drop(const InstructionOperand* op) { |
- auto it = map().find(op); |
- if (it != map().end()) map().erase(it); |
+void BlockAssessments::DropRegisters() { |
+ for (auto iterator = map().begin(), end = map().end(); iterator != end;) { |
+ auto current = iterator; |
+ ++iterator; |
+ InstructionOperand op = current->first; |
+ if (op.IsAnyRegister()) map().erase(current); |
} |
+} |
- void DropRegisters(const RegisterConfiguration* config) { |
- // TODO(dcarney): sort map by kind and drop range. |
- for (auto it = map().begin(); it != map().end();) { |
- const InstructionOperand* op = it->first; |
- if (op->IsRegister() || op->IsDoubleRegister()) { |
- map().erase(it++); |
- } else { |
- ++it; |
+BlockAssessments* RegisterAllocatorVerifier::CreateForBlock( |
+ const InstructionBlock* block) { |
+ RpoNumber current_block_id = block->rpo_number(); |
+ |
+ BlockAssessments* ret = new (zone()) BlockAssessments(zone()); |
+ if (block->PredecessorCount() == 0) { |
+ // TODO(mtrofin): the following check should hold, however, in certain |
+ // unit tests it is invalidated by the last block. Investigate and |
+ // normalize the CFG. |
+ // CHECK(current_block_id.ToInt() == 0); |
+ // The phi size test below is because we can, technically, have phi |
+ // instructions with one argument. Some tests expose that, too. |
+ } else if (block->PredecessorCount() == 1 && block->phis().size() == 0) { |
+ const BlockAssessments* prev_block = assessments_[block->predecessors()[0]]; |
+ ret->CopyFrom(prev_block); |
+ } else { |
+ for (RpoNumber pred_id : block->predecessors()) { |
+ // For every operand coming from any of the predecessors, create an |
+ // Unfinalized assessment. |
+ auto iterator = assessments_.find(pred_id); |
+ if (iterator == assessments_.end()) { |
+ // This block is the head of a loop, and this predecessor is the |
+ // loopback |
+ // arc. |
+ // Validate this is a loop case, otherwise the CFG is malformed. |
+ CHECK(pred_id >= current_block_id); |
+ CHECK(block->IsLoopHeader()); |
+ continue; |
+ } |
+ const BlockAssessments* pred_assessments = iterator->second; |
+ CHECK_NOT_NULL(pred_assessments); |
+ for (auto pair : pred_assessments->map()) { |
+ InstructionOperand operand = pair.first; |
+ if (ret->map().find(operand) == ret->map().end()) { |
+ ret->map().insert(std::make_pair( |
+ operand, new (zone()) PendingAssessment(block, operand))); |
+ } |
} |
} |
} |
+ return ret; |
+} |
- MapValue* Define(Zone* zone, const InstructionOperand* op, |
- int virtual_register) { |
- MapValue* value = new (zone) MapValue(); |
- value->define_vreg = virtual_register; |
- auto res = map().insert(std::make_pair(op, value)); |
- if (!res.second) res.first->second = value; |
- return value; |
- } |
- |
- void Use(const InstructionOperand* op, int use_vreg, bool initial_pass) { |
- auto it = map().find(op); |
- CHECK(it != map().end()); |
- MapValue* v = it->second; |
- if (v->define_vreg != kInvalidVreg) { |
- CHECK_EQ(v->define_vreg, use_vreg); |
- } |
- // Already used this vreg in this block. |
- if (v->use_vreg != kInvalidVreg) { |
- CHECK_EQ(v->use_vreg, use_vreg); |
- return; |
- } |
- if (!initial_pass) { |
- // A value may be defined and used in this block or the use must have |
- // propagated up. |
- if (v->succ_vreg != kInvalidVreg) { |
- CHECK_EQ(v->succ_vreg, use_vreg); |
- } else { |
- CHECK_EQ(v->define_vreg, use_vreg); |
+void RegisterAllocatorVerifier::ValidatePendingAssessment( |
+ RpoNumber block_id, InstructionOperand op, PendingAssessment* assessment, |
+ int virtual_register) { |
+ // When validating a pending assessment, it is possible some of the |
+ // assessments |
+ // for the original operand (the one where the assessment was created for |
+ // first) are also pending. To avoid recursion, we use a work list. To |
+ // deal with cycles, we keep a set of seen nodes. |
+ CHECK(worklist_.empty()); |
+ CHECK(seen_.empty()); |
+ worklist_.push(std::make_pair(assessment, virtual_register)); |
+ seen_.insert(block_id); |
+ |
+ while (!worklist_.empty()) { |
+ auto work = worklist_.front(); |
+ PendingAssessment* current_assessment = work.first; |
+ int current_virtual_register = work.second; |
+ InstructionOperand current_operand = current_assessment->operand(); |
+ worklist_.pop(); |
+ |
+ const InstructionBlock* origin = current_assessment->origin(); |
+ CHECK(origin->PredecessorCount() > 1 || origin->phis().size() > 0); |
+ |
+ // Check if the virtual register is a phi first, instead of relying on |
+ // the incoming assessments. In particular, this handles the case |
+ // v1 = phi v0 v0, which structurally is identical to v0 having been |
+ // defined at the top of a diamond, and arriving at the node joining the |
+ // diamond's branches. |
+ const PhiInstruction* phi = nullptr; |
+ for (const PhiInstruction* candidate : origin->phis()) { |
+ if (candidate->virtual_register() == current_virtual_register) { |
+ phi = candidate; |
+ break; |
} |
- // Mark the use. |
- it->second->use_vreg = use_vreg; |
- return; |
} |
- // Go up block list and ensure the correct definition is reached. |
- for (; v != nullptr; v = v->incoming) { |
- // Value unused in block. |
- if (v->define_vreg == kInvalidVreg && v->use_vreg == kInvalidVreg) { |
- continue; |
- } |
- // Found correct definition or use. |
- CHECK(v->define_vreg == use_vreg || v->use_vreg == use_vreg); |
- // Mark the use. |
- it->second->use_vreg = use_vreg; |
- return; |
- } |
- // Use of a non-phi value without definition. |
- CHECK(false); |
- } |
- void UsePhi(const InstructionOperand* op, const PhiData* phi, |
- bool initial_pass) { |
- auto it = map().find(op); |
- CHECK(it != map().end()); |
- MapValue* v = it->second; |
- int use_vreg = phi->virtual_register; |
- // Phis are not defined. |
- CHECK_EQ(kInvalidVreg, v->define_vreg); |
- // Already used this vreg in this block. |
- if (v->use_vreg != kInvalidVreg) { |
- CHECK_EQ(v->use_vreg, use_vreg); |
- return; |
- } |
- if (!initial_pass) { |
- // A used phi must have propagated its use to a predecessor. |
- CHECK_EQ(v->succ_vreg, use_vreg); |
- // Mark the use. |
- v->use_vreg = use_vreg; |
- return; |
- } |
- // Go up the block list starting at the first predecessor and ensure this |
- // phi has a correct use or definition. |
- for (v = v->incoming; v != nullptr; v = v->incoming) { |
- // Value unused in block. |
- if (v->define_vreg == kInvalidVreg && v->use_vreg == kInvalidVreg) { |
- continue; |
- } |
- // Found correct definition or use. |
- if (v->define_vreg != kInvalidVreg) { |
- CHECK(v->define_vreg == phi->first_pred_vreg); |
- } else if (v->use_vreg != phi->first_pred_vreg) { |
- // Walk the phi chain, hunting for a matching phi use. |
- const PhiData* p = phi; |
- for (; p != nullptr; p = p->first_pred_phi) { |
- if (p->virtual_register == v->use_vreg) break; |
+ int op_index = 0; |
+ for (RpoNumber pred : origin->predecessors()) { |
+ int expected = |
+ phi != nullptr ? phi->operands()[op_index] : current_virtual_register; |
+ |
+ ++op_index; |
+ auto pred_assignment = assessments_.find(pred); |
+ if (pred_assignment == assessments_.end()) { |
+ CHECK(origin->IsLoopHeader()); |
+ auto todo_iter = outstanding_assessments_.find(pred); |
+ DelayedAssessments* set = nullptr; |
+ if (todo_iter == outstanding_assessments_.end()) { |
+ set = new (zone()) DelayedAssessments(zone()); |
+ outstanding_assessments_.insert(std::make_pair(pred, set)); |
+ } else { |
+ set = todo_iter->second; |
} |
- CHECK(p); |
+ set->AddDelayedAssessment(current_operand, expected); |
+ continue; |
} |
- // Mark the use. |
- it->second->use_vreg = use_vreg; |
- return; |
- } |
- // Use of a phi value without definition. |
- UNREACHABLE(); |
- } |
- |
- private: |
- Map map_; |
- DISALLOW_COPY_AND_ASSIGN(OperandMap); |
-}; |
-} // namespace |
+ const BlockAssessments* pred_assessments = pred_assignment->second; |
+ auto found_contribution = pred_assessments->map().find(current_operand); |
+ CHECK(found_contribution != pred_assessments->map().end()); |
+ Assessment* contribution = found_contribution->second; |
- |
-class RegisterAllocatorVerifier::BlockMaps { |
- public: |
- BlockMaps(Zone* zone, const InstructionSequence* sequence) |
- : zone_(zone), |
- sequence_(sequence), |
- phi_map_guard_(sequence->VirtualRegisterCount(), zone), |
- phi_map_(zone), |
- incoming_maps_(zone), |
- outgoing_maps_(zone) { |
- InitializePhis(); |
- InitializeOperandMaps(); |
- } |
- |
- bool IsPhi(int virtual_register) { |
- return phi_map_guard_.Contains(virtual_register); |
- } |
- |
- const PhiData* GetPhi(int virtual_register) { |
- auto it = phi_map_.find(virtual_register); |
- CHECK(it != phi_map_.end()); |
- return it->second; |
- } |
- |
- OperandMap* InitializeIncoming(size_t block_index, bool initial_pass) { |
- return initial_pass ? InitializeFromFirstPredecessor(block_index) |
- : InitializeFromIntersection(block_index); |
- } |
- |
- void PropagateUsesBackwards() { |
- typedef std::set<size_t, std::greater<size_t>, zone_allocator<size_t>> |
- BlockIds; |
- BlockIds block_ids((BlockIds::key_compare()), |
- zone_allocator<size_t>(zone())); |
- // First ensure that incoming contains only keys in all predecessors. |
- for (const InstructionBlock* block : sequence()->instruction_blocks()) { |
- size_t index = block->rpo_number().ToSize(); |
- block_ids.insert(index); |
- OperandMap::Map& succ_map = incoming_maps_[index]->map(); |
- for (size_t i = 0; i < block->PredecessorCount(); ++i) { |
- RpoNumber pred_rpo = block->predecessors()[i]; |
- succ_map.Intersect(outgoing_maps_[pred_rpo.ToSize()]->map()); |
- } |
- } |
- // Back propagation fixpoint. |
- while (!block_ids.empty()) { |
- // Pop highest block_id. |
- auto block_id_it = block_ids.begin(); |
- const size_t succ_index = *block_id_it; |
- block_ids.erase(block_id_it); |
- // Propagate uses back to their definition blocks using succ_vreg. |
- const InstructionBlock* block = |
- sequence()->instruction_blocks()[succ_index]; |
- OperandMap::Map& succ_map = incoming_maps_[succ_index]->map(); |
- for (size_t i = 0; i < block->PredecessorCount(); ++i) { |
- for (auto& succ_val : succ_map) { |
- // An incoming map contains no defines. |
- CHECK_EQ(kInvalidVreg, succ_val.second->define_vreg); |
- // Compute succ_vreg. |
- int succ_vreg = succ_val.second->succ_vreg; |
- if (succ_vreg == kInvalidVreg) { |
- succ_vreg = succ_val.second->use_vreg; |
- // Initialize succ_vreg in back propagation chain. |
- succ_val.second->succ_vreg = succ_vreg; |
- } |
- if (succ_vreg == kInvalidVreg) continue; |
- // May need to transition phi. |
- if (IsPhi(succ_vreg)) { |
- const PhiData* phi = GetPhi(succ_vreg); |
- if (phi->definition_rpo.ToSize() == succ_index) { |
- // phi definition block, transition to pred value. |
- succ_vreg = phi->operands[i]; |
- } |
- } |
- // Push succ_vreg up to all predecessors. |
- RpoNumber pred_rpo = block->predecessors()[i]; |
- OperandMap::Map& pred_map = outgoing_maps_[pred_rpo.ToSize()]->map(); |
- auto& pred_val = *pred_map.find(succ_val.first); |
- if (pred_val.second->use_vreg != kInvalidVreg) { |
- CHECK_EQ(succ_vreg, pred_val.second->use_vreg); |
- } |
- if (pred_val.second->define_vreg != kInvalidVreg) { |
- CHECK_EQ(succ_vreg, pred_val.second->define_vreg); |
- } |
- if (pred_val.second->succ_vreg != kInvalidVreg) { |
- if (succ_vreg != pred_val.second->succ_vreg) { |
- // When a block introduces 2 identical phis A and B, and both are |
- // operands to other phis C and D, and we optimized the moves |
- // defining A or B such that they now appear in the block defining |
- // A and B, the back propagation will get confused when visiting |
- // upwards from C and D. The operand in the block defining A and B |
- // will be attributed to C (or D, depending which of these is |
- // visited first). |
- CHECK(IsPhi(pred_val.second->succ_vreg)); |
- CHECK(IsPhi(succ_vreg)); |
- const PhiData* current_phi = GetPhi(succ_vreg); |
- const PhiData* assigned_phi = GetPhi(pred_val.second->succ_vreg); |
- CHECK_EQ(current_phi->operands.size(), |
- assigned_phi->operands.size()); |
- CHECK_EQ(current_phi->definition_rpo, |
- assigned_phi->definition_rpo); |
- for (size_t i = 0; i < current_phi->operands.size(); ++i) { |
- CHECK_EQ(current_phi->operands[i], assigned_phi->operands[i]); |
- } |
- } |
- } else { |
- pred_val.second->succ_vreg = succ_vreg; |
- block_ids.insert(pred_rpo.ToSize()); |
+ switch (contribution->kind()) { |
+ case Final: |
+ CHECK_EQ(FinalAssessment::cast(contribution)->virtual_register(), |
+ expected); |
+ break; |
+ case Pending: { |
+ // This happens if we have a diamond feeding into another one, and |
+ // the inner one never being used - other than for carrying the value. |
+ PendingAssessment* next = PendingAssessment::cast(contribution); |
+ if (seen_.find(pred) == seen_.end()) { |
+ worklist_.push({next, expected}); |
+ seen_.insert(pred); |
} |
+ // Note that we do not want to finalize pending assessments at the |
+ // beginning of a block - which is the information we'd have |
+ // available here. This is because this operand may be reused to |
+ // define |
+ // duplicate phis. |
+ break; |
} |
} |
} |
- // Clear uses and back links for second pass. |
- for (OperandMap* operand_map : incoming_maps_) { |
- for (auto& succ_val : operand_map->map()) { |
- succ_val.second->incoming = nullptr; |
- succ_val.second->use_vreg = kInvalidVreg; |
- } |
- } |
- } |
- |
- private: |
- OperandMap* InitializeFromFirstPredecessor(size_t block_index) { |
- OperandMap* to_init = outgoing_maps_[block_index]; |
- CHECK(to_init->map().empty()); |
- const InstructionBlock* block = |
- sequence()->instruction_blocks()[block_index]; |
- if (block->predecessors().empty()) return to_init; |
- size_t predecessor_index = block->predecessors()[0].ToSize(); |
- // Ensure not a backedge. |
- CHECK(predecessor_index < block->rpo_number().ToSize()); |
- OperandMap* incoming = outgoing_maps_[predecessor_index]; |
- // Copy map and replace values. |
- to_init->map() = incoming->map(); |
- for (auto& it : to_init->map()) { |
- OperandMap::MapValue* incoming = it.second; |
- it.second = new (zone()) OperandMap::MapValue(); |
- it.second->incoming = incoming; |
- } |
- // Copy to incoming map for second pass. |
- incoming_maps_[block_index]->map() = to_init->map(); |
- return to_init; |
- } |
- |
- OperandMap* InitializeFromIntersection(size_t block_index) { |
- return incoming_maps_[block_index]; |
- } |
- |
- void InitializeOperandMaps() { |
- size_t block_count = sequence()->instruction_blocks().size(); |
- incoming_maps_.reserve(block_count); |
- outgoing_maps_.reserve(block_count); |
- for (size_t i = 0; i < block_count; ++i) { |
- incoming_maps_.push_back(new (zone()) OperandMap(zone())); |
- outgoing_maps_.push_back(new (zone()) OperandMap(zone())); |
- } |
} |
+ seen_.clear(); |
+ CHECK(worklist_.empty()); |
+} |
- void InitializePhis() { |
- const size_t block_count = sequence()->instruction_blocks().size(); |
- for (size_t block_index = 0; block_index < block_count; ++block_index) { |
- const InstructionBlock* block = |
- sequence()->instruction_blocks()[block_index]; |
- for (const PhiInstruction* phi : block->phis()) { |
- int first_pred_vreg = phi->operands()[0]; |
- const PhiData* first_pred_phi = nullptr; |
- if (IsPhi(first_pred_vreg)) { |
- first_pred_phi = GetPhi(first_pred_vreg); |
- first_pred_vreg = first_pred_phi->first_pred_vreg; |
- } |
- CHECK(!IsPhi(first_pred_vreg)); |
- PhiData* phi_data = new (zone()) PhiData( |
- block->rpo_number(), phi, first_pred_vreg, first_pred_phi, zone()); |
- auto res = |
- phi_map_.insert(std::make_pair(phi->virtual_register(), phi_data)); |
- CHECK(res.second); |
- phi_map_guard_.Add(phi->virtual_register()); |
- } |
+void RegisterAllocatorVerifier::ValidateUse( |
+ RpoNumber block_id, BlockAssessments* current_assessments, |
+ InstructionOperand op, int virtual_register) { |
+ auto iterator = current_assessments->map().find(op); |
+ // We should have seen this operand before. |
+ CHECK(iterator != current_assessments->map().end()); |
+ Assessment* assessment = iterator->second; |
+ |
+ switch (assessment->kind()) { |
+ case Final: |
+ // The virtual registers should match. |
+ CHECK_EQ(FinalAssessment::cast(assessment)->virtual_register(), |
+ virtual_register); |
+ break; |
+ case Pending: { |
+ ValidatePendingAssessment( |
+ block_id, op, PendingAssessment::cast(assessment), virtual_register); |
+ // If everything checks out, we may make the assessment. |
+ current_assessments->map()[op] = |
+ new (zone()) FinalAssessment(virtual_register); |
+ break; |
} |
} |
- |
- typedef ZoneVector<OperandMap*> OperandMaps; |
- typedef ZoneVector<PhiData*> PhiVector; |
- |
- Zone* zone() const { return zone_; } |
- const InstructionSequence* sequence() const { return sequence_; } |
- |
- Zone* const zone_; |
- const InstructionSequence* const sequence_; |
- BitVector phi_map_guard_; |
- PhiMap phi_map_; |
- OperandMaps incoming_maps_; |
- OperandMaps outgoing_maps_; |
-}; |
- |
- |
-void RegisterAllocatorVerifier::VerifyGapMoves() { |
- BlockMaps block_maps(zone(), sequence()); |
- VerifyGapMoves(&block_maps, true); |
- block_maps.PropagateUsesBackwards(); |
- VerifyGapMoves(&block_maps, false); |
} |
- |
-// Compute and verify outgoing values for every block. |
-void RegisterAllocatorVerifier::VerifyGapMoves(BlockMaps* block_maps, |
- bool initial_pass) { |
+void RegisterAllocatorVerifier::VerifyGapMoves() { |
+ CHECK(assessments_.empty()); |
+ CHECK(outstanding_assessments_.empty()); |
const size_t block_count = sequence()->instruction_blocks().size(); |
for (size_t block_index = 0; block_index < block_count; ++block_index) { |
- OperandMap* current = |
- block_maps->InitializeIncoming(block_index, initial_pass); |
const InstructionBlock* block = |
sequence()->instruction_blocks()[block_index]; |
+ BlockAssessments* block_assessments = CreateForBlock(block); |
+ |
for (int instr_index = block->code_start(); instr_index < block->code_end(); |
++instr_index) { |
const InstructionConstraint& instr_constraint = constraints_[instr_index]; |
const Instruction* instr = instr_constraint.instruction_; |
- current->RunGaps(zone(), instr); |
+ block_assessments->PerformMoves(instr); |
+ |
const OperandConstraint* op_constraints = |
instr_constraint.operand_constraints_; |
size_t count = 0; |
@@ -724,24 +484,19 @@ void RegisterAllocatorVerifier::VerifyGapMoves(BlockMaps* block_maps, |
continue; |
} |
int virtual_register = op_constraints[count].virtual_register_; |
- const InstructionOperand* op = instr->InputAt(i); |
- if (!block_maps->IsPhi(virtual_register)) { |
- current->Use(op, virtual_register, initial_pass); |
- } else { |
- const PhiData* phi = block_maps->GetPhi(virtual_register); |
- current->UsePhi(op, phi, initial_pass); |
- } |
+ InstructionOperand op = *instr->InputAt(i); |
+ ValidateUse(block->rpo_number(), block_assessments, op, |
+ virtual_register); |
} |
for (size_t i = 0; i < instr->TempCount(); ++i, ++count) { |
- current->Drop(instr->TempAt(i)); |
+ block_assessments->Drop(*instr->TempAt(i)); |
} |
if (instr->IsCall()) { |
- current->DropRegisters(config()); |
+ block_assessments->DropRegisters(); |
} |
for (size_t i = 0; i < instr->OutputCount(); ++i, ++count) { |
int virtual_register = op_constraints[count].virtual_register_; |
- OperandMap::MapValue* value = |
- current->Define(zone(), instr->OutputAt(i), virtual_register); |
+ block_assessments->AddDefinition(*instr->OutputAt(i), virtual_register); |
if (op_constraints[count].type_ == kRegisterAndSlot) { |
const AllocatedOperand* reg_op = |
AllocatedOperand::cast(instr->OutputAt(i)); |
@@ -749,13 +504,35 @@ void RegisterAllocatorVerifier::VerifyGapMoves(BlockMaps* block_maps, |
const AllocatedOperand* stack_op = AllocatedOperand::New( |
zone(), LocationOperand::LocationKind::STACK_SLOT, rep, |
op_constraints[i].spilled_slot_); |
- auto insert_result = |
- current->map().insert(std::make_pair(stack_op, value)); |
- DCHECK(insert_result.second); |
- USE(insert_result); |
+ block_assessments->AddDefinition(*stack_op, virtual_register); |
} |
} |
} |
+ // Now commit the assessments for this block. If there are any delayed |
+ // assessments, ValidatePendingAssessment should see this block, too. |
+ assessments_[block->rpo_number()] = block_assessments; |
+ |
+ auto todo_iter = outstanding_assessments_.find(block->rpo_number()); |
+ if (todo_iter == outstanding_assessments_.end()) continue; |
+ DelayedAssessments* todo = todo_iter->second; |
+ for (auto pair : todo->map()) { |
+ InstructionOperand op = pair.first; |
+ int vreg = pair.second; |
+ auto found_op = block_assessments->map().find(op); |
+ CHECK(found_op != block_assessments->map().end()); |
+ switch (found_op->second->kind()) { |
+ case Final: |
+ CHECK(FinalAssessment::cast(found_op->second)->virtual_register() == |
+ vreg); |
+ break; |
+ case Pending: |
+ ValidatePendingAssessment(block->rpo_number(), op, |
+ PendingAssessment::cast(found_op->second), |
+ vreg); |
+ block_assessments->map()[op] = new (zone()) FinalAssessment(vreg); |
+ break; |
+ } |
+ } |
} |
} |