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