Index: src/compiler/register-allocator-verifier.h |
diff --git a/src/compiler/register-allocator-verifier.h b/src/compiler/register-allocator-verifier.h |
index f3ab54f01818788ae70fb2a6aa59714aefd9e776..18d38c58a1511af434eeaf290c9d69a8a083f282 100644 |
--- a/src/compiler/register-allocator-verifier.h |
+++ b/src/compiler/register-allocator-verifier.h |
@@ -14,6 +14,145 @@ namespace compiler { |
class InstructionOperand; |
class InstructionSequence; |
+// The register allocator validator traverses instructions in the instruction |
+// sequence, and verifies the correctness of machine operand substitutions of |
+// virtual registers. It collects the virtual register instruction signatures |
+// before register allocation. Then, after the register allocation pipeline |
+// completes, it compares the operand substitutions against the pre-allocation |
+// data. |
+// At a high level, validation works as follows: we iterate through each block, |
+// and, in a block, through each instruction; then: |
+// - when an operand is the output of an instruction, we associate it to the |
+// virtual register that the instruction sequence declares as its output. We |
+// use the concept of "FinalAssessment" to model this. |
+// - when an operand is used in an instruction, we check that the assessment |
+// matches the expectation of the instruction |
+// - moves simply copy the assessment over to the new operand |
+// - blocks with more than one predecessor associate to each operand a "Pending" |
+// assessment. The pending assessment remembers the operand and block where it |
+// was created. Then, when the value is used (which may be as a different |
+// operand, because of moves), we check that the virtual register at the use |
+// site matches the definition of this pending operand: either the phi inputs |
+// match, or, if it's not a phi, all the predecessors at the point the pending |
+// assessment was defined have that operand assigned to the given virtual |
+// register. |
+// If a block is a loop header - so one or more of its predecessors are it or |
+// below - we still treat uses of operands as above, but we record which operand |
+// assessments haven't been made yet, and what virtual register they must |
+// correspond to, and verify that when we are done with the respective |
+// predecessor blocks. |
+// This way, the algorithm always makes a final decision about the operands |
+// in an instruction, ensuring convergence. |
+// Operand assessments are recorded per block, as the result at the exit from |
+// the block. When moving to a new block, we copy assessments from its single |
+// predecessor, or, if the block has multiple predecessors, the mechanism was |
+// described already. |
+ |
+enum AssessmentKind { Final, Pending }; |
+ |
+class Assessment : public ZoneObject { |
+ public: |
+ AssessmentKind kind() const { return kind_; } |
+ |
+ protected: |
+ explicit Assessment(AssessmentKind kind) : kind_(kind) {} |
+ AssessmentKind kind_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Assessment); |
+}; |
+ |
+// FinalAssessmens are associated to operands that we know to be a certain |
+// virtual register. |
+class FinalAssessment final : public Assessment { |
+ public: |
+ explicit FinalAssessment(int virtual_register) |
+ : Assessment(Final), virtual_register_(virtual_register) {} |
+ |
+ int virtual_register() const { return virtual_register_; } |
+ static const FinalAssessment* cast(const Assessment* assessment) { |
+ CHECK(assessment->kind() == Final); |
+ return static_cast<const FinalAssessment*>(assessment); |
+ } |
+ |
+ private: |
+ int virtual_register_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FinalAssessment); |
+}; |
+ |
+// PendingAssessments are associated to operands coming from the multiple |
+// predecessors of a block. We only record the operand and the block, and |
+// will determine if the way the operand is defined (from the predecessors) |
+// matches a particular use. This handles scenarios where multiple phis are |
+// defined with identical operands, and the move optimizer moved down the moves |
+// separating the 2 phis in the block defining them. |
+class PendingAssessment final : public Assessment { |
+ public: |
+ explicit PendingAssessment(const InstructionBlock* origin, |
+ InstructionOperand operand) |
+ : Assessment(Pending), origin_(origin), operand_(operand) {} |
+ |
+ static PendingAssessment* cast(Assessment* assessment) { |
+ CHECK(assessment->kind() == Pending); |
+ return static_cast<PendingAssessment*>(assessment); |
+ } |
+ |
+ const InstructionBlock* origin() const { return origin_; } |
+ InstructionOperand operand() const { return operand_; } |
+ |
+ private: |
+ const InstructionBlock* const origin_; |
+ InstructionOperand operand_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PendingAssessment); |
+}; |
+ |
+struct OperandAsKeyLess { |
+ bool operator()(const InstructionOperand& a, |
+ const InstructionOperand& b) const { |
+ return a.CompareCanonicalized(b); |
+ } |
+}; |
+ |
+// Assessments associated with a basic block. |
+class BlockAssessments : public ZoneObject { |
+ public: |
+ typedef ZoneMap<InstructionOperand, Assessment*, OperandAsKeyLess> OperandMap; |
+ explicit BlockAssessments(Zone* zone) |
+ : map_(zone), map_for_moves_(zone), zone_(zone) {} |
+ void Drop(InstructionOperand operand) { map_.erase(operand); } |
+ void DropRegisters(); |
+ void AddDefinition(InstructionOperand operand, int virtual_register) { |
+ auto existent = map_.find(operand); |
+ if (existent != map_.end()) { |
+ // Drop the assignment |
+ map_.erase(existent); |
+ } |
+ map_.insert( |
+ std::make_pair(operand, new (zone_) FinalAssessment(virtual_register))); |
+ } |
+ |
+ void PerformMoves(const Instruction* instruction); |
+ void PerformParallelMoves(const ParallelMove* moves); |
+ void CopyFrom(const BlockAssessments* other) { |
+ CHECK(map_.empty()); |
+ CHECK_NOT_NULL(other); |
+ map_.insert(other->map_.begin(), other->map_.end()); |
+ } |
+ |
+ OperandMap& map() { return map_; } |
+ const OperandMap& map() const { return map_; } |
+ void Print() const; |
+ |
+ private: |
+ OperandMap map_; |
+ OperandMap map_for_moves_; |
+ Zone* zone_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BlockAssessments); |
+}; |
+ |
class RegisterAllocatorVerifier final : public ZoneObject { |
public: |
RegisterAllocatorVerifier(Zone* zone, const RegisterConfiguration* config, |
@@ -53,10 +192,29 @@ class RegisterAllocatorVerifier final : public ZoneObject { |
OperandConstraint* operand_constraints_; |
}; |
- class BlockMaps; |
- |
typedef ZoneVector<InstructionConstraint> Constraints; |
+ class DelayedAssessments : public ZoneObject { |
+ public: |
+ explicit DelayedAssessments(Zone* zone) : map_(zone) {} |
+ |
+ const ZoneMap<InstructionOperand, int, OperandAsKeyLess>& map() const { |
+ return map_; |
+ } |
+ |
+ void AddDelayedAssessment(InstructionOperand op, int vreg) { |
+ auto it = map_.find(op); |
+ if (it == map_.end()) { |
+ map_.insert(std::make_pair(op, vreg)); |
+ } else { |
+ CHECK_EQ(it->second, vreg); |
+ } |
+ } |
+ |
+ private: |
+ ZoneMap<InstructionOperand, int, OperandAsKeyLess> map_; |
+ }; |
+ |
Zone* zone() const { return zone_; } |
const RegisterConfiguration* config() { return config_; } |
const InstructionSequence* sequence() const { return sequence_; } |
@@ -70,13 +228,25 @@ class RegisterAllocatorVerifier final : public ZoneObject { |
OperandConstraint* constraint); |
void CheckConstraint(const InstructionOperand* op, |
const OperandConstraint* constraint); |
+ BlockAssessments* CreateForBlock(const InstructionBlock* block); |
- void VerifyGapMoves(BlockMaps* outgoing_mappings, bool initial_pass); |
+ void ValidatePendingAssessment(RpoNumber block_id, InstructionOperand op, |
+ PendingAssessment* assessment, |
+ int virtual_register); |
+ void ValidateUse(RpoNumber block_id, BlockAssessments* current_assessments, |
+ InstructionOperand op, int virtual_register); |
Zone* const zone_; |
const RegisterConfiguration* config_; |
const InstructionSequence* const sequence_; |
Constraints constraints_; |
+ ZoneMap<RpoNumber, BlockAssessments*> assessments_; |
+ ZoneMap<RpoNumber, DelayedAssessments*> outstanding_assessments_; |
+ |
+ // Cached structures, to avoid memory churn. Needed solely in |
+ // ValidatePendingAssessment. |
+ ZoneQueue<std::pair<PendingAssessment*, int>> worklist_; |
+ ZoneSet<RpoNumber> seen_; |
DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorVerifier); |
}; |