OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef V8_REGISTER_ALLOCATOR_VERIFIER_H_ | 5 #ifndef V8_REGISTER_ALLOCATOR_VERIFIER_H_ |
6 #define V8_REGISTER_ALLOCATOR_VERIFIER_H_ | 6 #define V8_REGISTER_ALLOCATOR_VERIFIER_H_ |
7 | 7 |
8 #include "src/zone-containers.h" | 8 #include "src/zone-containers.h" |
9 | 9 |
10 namespace v8 { | 10 namespace v8 { |
11 namespace internal { | 11 namespace internal { |
12 namespace compiler { | 12 namespace compiler { |
13 | 13 |
14 class InstructionOperand; | 14 class InstructionOperand; |
15 class InstructionSequence; | 15 class InstructionSequence; |
16 | 16 |
| 17 // The register allocator validator traverses instructions in the instruction |
| 18 // sequence, and verifies the correctness of machine operand substitutions of |
| 19 // virtual registers. It collects the virtual register instruction signatures |
| 20 // before register allocation. Then, after the register allocation pipeline |
| 21 // completes, it compares the operand substitutions against the pre-allocation |
| 22 // data. |
| 23 // At a high level, validation works as follows: we iterate through each block, |
| 24 // and, in a block, through each instruction; then: |
| 25 // - when an operand is the output of an instruction, we associate it to the |
| 26 // virtual register that the instruction sequence declares as its output. We |
| 27 // use the concept of "FinalAssessment" to model this. |
| 28 // - when an operand is used in an instruction, we check that the assessment |
| 29 // matches the expectation of the instruction |
| 30 // - moves simply copy the assessment over to the new operand |
| 31 // - blocks with more than one predecessor associate to each operand a "Pending" |
| 32 // assessment. The pending assessment remembers the operand and block where it |
| 33 // was created. Then, when the value is used (which may be as a different |
| 34 // operand, because of moves), we check that the virtual register at the use |
| 35 // site matches the definition of this pending operand: either the phi inputs |
| 36 // match, or, if it's not a phi, all the predecessors at the point the pending |
| 37 // assessment was defined have that operand assigned to the given virtual |
| 38 // register. |
| 39 // If a block is a loop header - so one or more of its predecessors are it or |
| 40 // below - we still treat uses of operands as above, but we record which operand |
| 41 // assessments haven't been made yet, and what virtual register they must |
| 42 // correspond to, and verify that when we are done with the respective |
| 43 // predecessor blocks. |
| 44 // This way, the algorithm always makes a final decision about the operands |
| 45 // in an instruction, ensuring convergence. |
| 46 // Operand assessments are recorded per block, as the result at the exit from |
| 47 // the block. When moving to a new block, we copy assessments from its single |
| 48 // predecessor, or, if the block has multiple predecessors, the mechanism was |
| 49 // described already. |
| 50 |
| 51 enum AssessmentKind { Final, Pending }; |
| 52 |
| 53 class Assessment : public ZoneObject { |
| 54 public: |
| 55 AssessmentKind kind() const { return kind_; } |
| 56 |
| 57 protected: |
| 58 explicit Assessment(AssessmentKind kind) : kind_(kind) {} |
| 59 AssessmentKind kind_; |
| 60 |
| 61 private: |
| 62 DISALLOW_COPY_AND_ASSIGN(Assessment); |
| 63 }; |
| 64 |
| 65 // FinalAssessmens are associated to operands that we know to be a certain |
| 66 // virtual register. |
| 67 class FinalAssessment final : public Assessment { |
| 68 public: |
| 69 explicit FinalAssessment(int virtual_register) |
| 70 : Assessment(Final), virtual_register_(virtual_register) {} |
| 71 |
| 72 int virtual_register() const { return virtual_register_; } |
| 73 static const FinalAssessment* cast(const Assessment* assessment) { |
| 74 CHECK(assessment->kind() == Final); |
| 75 return static_cast<const FinalAssessment*>(assessment); |
| 76 } |
| 77 |
| 78 private: |
| 79 int virtual_register_; |
| 80 |
| 81 DISALLOW_COPY_AND_ASSIGN(FinalAssessment); |
| 82 }; |
| 83 |
| 84 // PendingAssessments are associated to operands coming from the multiple |
| 85 // predecessors of a block. We only record the operand and the block, and |
| 86 // will determine if the way the operand is defined (from the predecessors) |
| 87 // matches a particular use. This handles scenarios where multiple phis are |
| 88 // defined with identical operands, and the move optimizer moved down the moves |
| 89 // separating the 2 phis in the block defining them. |
| 90 class PendingAssessment final : public Assessment { |
| 91 public: |
| 92 explicit PendingAssessment(const InstructionBlock* origin, |
| 93 InstructionOperand operand) |
| 94 : Assessment(Pending), origin_(origin), operand_(operand) {} |
| 95 |
| 96 static PendingAssessment* cast(Assessment* assessment) { |
| 97 CHECK(assessment->kind() == Pending); |
| 98 return static_cast<PendingAssessment*>(assessment); |
| 99 } |
| 100 |
| 101 const InstructionBlock* origin() const { return origin_; } |
| 102 InstructionOperand operand() const { return operand_; } |
| 103 |
| 104 private: |
| 105 const InstructionBlock* const origin_; |
| 106 InstructionOperand operand_; |
| 107 |
| 108 DISALLOW_COPY_AND_ASSIGN(PendingAssessment); |
| 109 }; |
| 110 |
| 111 struct OperandAsKeyLess { |
| 112 bool operator()(const InstructionOperand& a, |
| 113 const InstructionOperand& b) const { |
| 114 return a.CompareCanonicalized(b); |
| 115 } |
| 116 }; |
| 117 |
| 118 // Assessments associated with a basic block. |
| 119 class BlockAssessments : public ZoneObject { |
| 120 public: |
| 121 typedef ZoneMap<InstructionOperand, Assessment*, OperandAsKeyLess> OperandMap; |
| 122 explicit BlockAssessments(Zone* zone) |
| 123 : map_(zone), map_for_moves_(zone), zone_(zone) {} |
| 124 void Drop(InstructionOperand operand) { map_.erase(operand); } |
| 125 void DropRegisters(); |
| 126 void AddDefinition(InstructionOperand operand, int virtual_register) { |
| 127 auto existent = map_.find(operand); |
| 128 if (existent != map_.end()) { |
| 129 // Drop the assignment |
| 130 map_.erase(existent); |
| 131 } |
| 132 map_.insert( |
| 133 std::make_pair(operand, new (zone_) FinalAssessment(virtual_register))); |
| 134 } |
| 135 |
| 136 void PerformMoves(const Instruction* instruction); |
| 137 void PerformParallelMoves(const ParallelMove* moves); |
| 138 void CopyFrom(const BlockAssessments* other) { |
| 139 CHECK(map_.empty()); |
| 140 CHECK_NOT_NULL(other); |
| 141 map_.insert(other->map_.begin(), other->map_.end()); |
| 142 } |
| 143 |
| 144 OperandMap& map() { return map_; } |
| 145 const OperandMap& map() const { return map_; } |
| 146 void Print() const; |
| 147 |
| 148 private: |
| 149 OperandMap map_; |
| 150 OperandMap map_for_moves_; |
| 151 Zone* zone_; |
| 152 |
| 153 DISALLOW_COPY_AND_ASSIGN(BlockAssessments); |
| 154 }; |
| 155 |
17 class RegisterAllocatorVerifier final : public ZoneObject { | 156 class RegisterAllocatorVerifier final : public ZoneObject { |
18 public: | 157 public: |
19 RegisterAllocatorVerifier(Zone* zone, const RegisterConfiguration* config, | 158 RegisterAllocatorVerifier(Zone* zone, const RegisterConfiguration* config, |
20 const InstructionSequence* sequence); | 159 const InstructionSequence* sequence); |
21 | 160 |
22 void VerifyAssignment(); | 161 void VerifyAssignment(); |
23 void VerifyGapMoves(); | 162 void VerifyGapMoves(); |
24 | 163 |
25 private: | 164 private: |
26 enum ConstraintType { | 165 enum ConstraintType { |
(...skipping 19 matching lines...) Expand all Loading... |
46 int spilled_slot_; | 185 int spilled_slot_; |
47 int virtual_register_; | 186 int virtual_register_; |
48 }; | 187 }; |
49 | 188 |
50 struct InstructionConstraint { | 189 struct InstructionConstraint { |
51 const Instruction* instruction_; | 190 const Instruction* instruction_; |
52 size_t operand_constaints_size_; | 191 size_t operand_constaints_size_; |
53 OperandConstraint* operand_constraints_; | 192 OperandConstraint* operand_constraints_; |
54 }; | 193 }; |
55 | 194 |
56 class BlockMaps; | 195 typedef ZoneVector<InstructionConstraint> Constraints; |
57 | 196 |
58 typedef ZoneVector<InstructionConstraint> Constraints; | 197 class DelayedAssessments : public ZoneObject { |
| 198 public: |
| 199 explicit DelayedAssessments(Zone* zone) : map_(zone) {} |
| 200 |
| 201 const ZoneMap<InstructionOperand, int, OperandAsKeyLess>& map() const { |
| 202 return map_; |
| 203 } |
| 204 |
| 205 void AddDelayedAssessment(InstructionOperand op, int vreg) { |
| 206 auto it = map_.find(op); |
| 207 if (it == map_.end()) { |
| 208 map_.insert(std::make_pair(op, vreg)); |
| 209 } else { |
| 210 CHECK_EQ(it->second, vreg); |
| 211 } |
| 212 } |
| 213 |
| 214 private: |
| 215 ZoneMap<InstructionOperand, int, OperandAsKeyLess> map_; |
| 216 }; |
59 | 217 |
60 Zone* zone() const { return zone_; } | 218 Zone* zone() const { return zone_; } |
61 const RegisterConfiguration* config() { return config_; } | 219 const RegisterConfiguration* config() { return config_; } |
62 const InstructionSequence* sequence() const { return sequence_; } | 220 const InstructionSequence* sequence() const { return sequence_; } |
63 Constraints* constraints() { return &constraints_; } | 221 Constraints* constraints() { return &constraints_; } |
64 | 222 |
65 static void VerifyInput(const OperandConstraint& constraint); | 223 static void VerifyInput(const OperandConstraint& constraint); |
66 static void VerifyTemp(const OperandConstraint& constraint); | 224 static void VerifyTemp(const OperandConstraint& constraint); |
67 static void VerifyOutput(const OperandConstraint& constraint); | 225 static void VerifyOutput(const OperandConstraint& constraint); |
68 | 226 |
69 void BuildConstraint(const InstructionOperand* op, | 227 void BuildConstraint(const InstructionOperand* op, |
70 OperandConstraint* constraint); | 228 OperandConstraint* constraint); |
71 void CheckConstraint(const InstructionOperand* op, | 229 void CheckConstraint(const InstructionOperand* op, |
72 const OperandConstraint* constraint); | 230 const OperandConstraint* constraint); |
| 231 BlockAssessments* CreateForBlock(const InstructionBlock* block); |
73 | 232 |
74 void VerifyGapMoves(BlockMaps* outgoing_mappings, bool initial_pass); | 233 void ValidatePendingAssessment(RpoNumber block_id, InstructionOperand op, |
| 234 PendingAssessment* assessment, |
| 235 int virtual_register); |
| 236 void ValidateUse(RpoNumber block_id, BlockAssessments* current_assessments, |
| 237 InstructionOperand op, int virtual_register); |
75 | 238 |
76 Zone* const zone_; | 239 Zone* const zone_; |
77 const RegisterConfiguration* config_; | 240 const RegisterConfiguration* config_; |
78 const InstructionSequence* const sequence_; | 241 const InstructionSequence* const sequence_; |
79 Constraints constraints_; | 242 Constraints constraints_; |
| 243 ZoneMap<RpoNumber, BlockAssessments*> assessments_; |
| 244 ZoneMap<RpoNumber, DelayedAssessments*> outstanding_assessments_; |
| 245 |
| 246 // Cached structures, to avoid memory churn. Needed solely in |
| 247 // ValidatePendingAssessment. |
| 248 ZoneQueue<std::pair<PendingAssessment*, int>> worklist_; |
| 249 ZoneSet<RpoNumber> seen_; |
80 | 250 |
81 DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorVerifier); | 251 DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorVerifier); |
82 }; | 252 }; |
83 | 253 |
84 } // namespace compiler | 254 } // namespace compiler |
85 } // namespace internal | 255 } // namespace internal |
86 } // namespace v8 | 256 } // namespace v8 |
87 | 257 |
88 #endif | 258 #endif |
OLD | NEW |