• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
kind()57   AssessmentKind kind() const { return kind_; }
58 
59  protected:
Assessment(AssessmentKind kind)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:
PendingAssessment(Zone * zone,const InstructionBlock * origin,InstructionOperand operand)76   explicit PendingAssessment(Zone* zone, const InstructionBlock* origin,
77                              InstructionOperand operand)
78       : Assessment(Pending),
79         origin_(origin),
80         operand_(operand),
81         aliases_(zone) {}
82 
cast(const Assessment * assessment)83   static const PendingAssessment* cast(const Assessment* assessment) {
84     CHECK(assessment->kind() == Pending);
85     return static_cast<const PendingAssessment*>(assessment);
86   }
87 
cast(Assessment * assessment)88   static PendingAssessment* cast(Assessment* assessment) {
89     CHECK(assessment->kind() == Pending);
90     return static_cast<PendingAssessment*>(assessment);
91   }
92 
origin()93   const InstructionBlock* origin() const { return origin_; }
operand()94   InstructionOperand operand() const { return operand_; }
IsAliasOf(int vreg)95   bool IsAliasOf(int vreg) const { return aliases_.count(vreg) > 0; }
AddAlias(int vreg)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:
FinalAssessment(int virtual_register)110   explicit FinalAssessment(int virtual_register)
111       : Assessment(Final), virtual_register_(virtual_register) {}
112 
virtual_register()113   int virtual_register() const { return virtual_register_; }
cast(const Assessment * assessment)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 {
operatorOperandAsKeyLess126   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;
BlockAssessments(Zone * zone)136   explicit BlockAssessments(Zone* zone)
137       : map_(zone), map_for_moves_(zone), zone_(zone) {}
Drop(InstructionOperand operand)138   void Drop(InstructionOperand operand) { map_.erase(operand); }
139   void DropRegisters();
AddDefinition(InstructionOperand operand,int virtual_register)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);
CopyFrom(const BlockAssessments * other)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 
map()158   OperandMap& map() { return map_; }
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:
DelayedAssessments(Zone * zone)215     explicit DelayedAssessments(Zone* zone) : map_(zone) {}
216 
map()217     const ZoneMap<InstructionOperand, int, OperandAsKeyLess>& map() const {
218       return map_;
219     }
220 
AddDelayedAssessment(InstructionOperand op,int vreg)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 
zone()234   Zone* zone() const { return zone_; }
config()235   const RegisterConfiguration* config() { return config_; }
sequence()236   const InstructionSequence* sequence() const { return sequence_; }
constraints()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