• 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_REGISTER_ALLOCATOR_VERIFIER_H_
6 #define V8_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.
40 // If a block is a loop header - so one or more of its predecessors are it or
41 // below - we still treat uses of operands as above, but we record which operand
42 // assessments haven't been made yet, and what virtual register they must
43 // correspond to, and verify that when we are done with the respective
44 // predecessor blocks.
45 // This way, the algorithm always makes a final decision about the operands
46 // in an instruction, ensuring convergence.
47 // Operand assessments are recorded per block, as the result at the exit from
48 // the block. When moving to a new block, we copy assessments from its single
49 // predecessor, or, if the block has multiple predecessors, the mechanism was
50 // described already.
51 
52 enum AssessmentKind { Final, Pending };
53 
54 class Assessment : public ZoneObject {
55  public:
kind()56   AssessmentKind kind() const { return kind_; }
57 
58  protected:
Assessment(AssessmentKind kind)59   explicit Assessment(AssessmentKind kind) : kind_(kind) {}
60   AssessmentKind kind_;
61 
62  private:
63   DISALLOW_COPY_AND_ASSIGN(Assessment);
64 };
65 
66 // PendingAssessments are associated to operands coming from the multiple
67 // predecessors of a block. We only record the operand and the block, and
68 // will determine if the way the operand is defined (from the predecessors)
69 // matches a particular use. This handles scenarios where multiple phis are
70 // defined with identical operands, and the move optimizer moved down the moves
71 // separating the 2 phis in the block defining them.
72 class PendingAssessment final : public Assessment {
73  public:
PendingAssessment(const InstructionBlock * origin,InstructionOperand operand)74   explicit PendingAssessment(const InstructionBlock* origin,
75                              InstructionOperand operand)
76       : Assessment(Pending), origin_(origin), operand_(operand) {}
77 
cast(const Assessment * assessment)78   static const PendingAssessment* cast(const Assessment* assessment) {
79     CHECK(assessment->kind() == Pending);
80     return static_cast<const PendingAssessment*>(assessment);
81   }
82 
origin()83   const InstructionBlock* origin() const { return origin_; }
operand()84   InstructionOperand operand() const { return operand_; }
85 
86  private:
87   const InstructionBlock* const origin_;
88   InstructionOperand operand_;
89 
90   DISALLOW_COPY_AND_ASSIGN(PendingAssessment);
91 };
92 
93 // FinalAssessments are associated to operands that we know to be a certain
94 // virtual register.
95 class FinalAssessment final : public Assessment {
96  public:
97   explicit FinalAssessment(int virtual_register,
98                            const PendingAssessment* original_pending = nullptr)
Assessment(Final)99       : Assessment(Final),
100         virtual_register_(virtual_register),
101         original_pending_assessment_(original_pending) {}
102 
virtual_register()103   int virtual_register() const { return virtual_register_; }
cast(const Assessment * assessment)104   static const FinalAssessment* cast(const Assessment* assessment) {
105     CHECK(assessment->kind() == Final);
106     return static_cast<const FinalAssessment*>(assessment);
107   }
108 
original_pending_assessment()109   const PendingAssessment* original_pending_assessment() const {
110     return original_pending_assessment_;
111   }
112 
113  private:
114   int virtual_register_;
115   const PendingAssessment* original_pending_assessment_;
116 
117   DISALLOW_COPY_AND_ASSIGN(FinalAssessment);
118 };
119 
120 struct OperandAsKeyLess {
operatorOperandAsKeyLess121   bool operator()(const InstructionOperand& a,
122                   const InstructionOperand& b) const {
123     return a.CompareCanonicalized(b);
124   }
125 };
126 
127 // Assessments associated with a basic block.
128 class BlockAssessments : public ZoneObject {
129  public:
130   typedef ZoneMap<InstructionOperand, Assessment*, OperandAsKeyLess> OperandMap;
BlockAssessments(Zone * zone)131   explicit BlockAssessments(Zone* zone)
132       : map_(zone), map_for_moves_(zone), zone_(zone) {}
Drop(InstructionOperand operand)133   void Drop(InstructionOperand operand) { map_.erase(operand); }
134   void DropRegisters();
AddDefinition(InstructionOperand operand,int virtual_register)135   void AddDefinition(InstructionOperand operand, int virtual_register) {
136     auto existent = map_.find(operand);
137     if (existent != map_.end()) {
138       // Drop the assignment
139       map_.erase(existent);
140     }
141     map_.insert(
142         std::make_pair(operand, new (zone_) FinalAssessment(virtual_register)));
143   }
144 
145   void PerformMoves(const Instruction* instruction);
146   void PerformParallelMoves(const ParallelMove* moves);
CopyFrom(const BlockAssessments * other)147   void CopyFrom(const BlockAssessments* other) {
148     CHECK(map_.empty());
149     CHECK_NOT_NULL(other);
150     map_.insert(other->map_.begin(), other->map_.end());
151   }
152 
map()153   OperandMap& map() { return map_; }
map()154   const OperandMap& map() const { return map_; }
155   void Print() const;
156 
157  private:
158   OperandMap map_;
159   OperandMap map_for_moves_;
160   Zone* zone_;
161 
162   DISALLOW_COPY_AND_ASSIGN(BlockAssessments);
163 };
164 
165 class RegisterAllocatorVerifier final : public ZoneObject {
166  public:
167   RegisterAllocatorVerifier(Zone* zone, const RegisterConfiguration* config,
168                             const InstructionSequence* sequence);
169 
170   void VerifyAssignment();
171   void VerifyGapMoves();
172 
173  private:
174   enum ConstraintType {
175     kConstant,
176     kImmediate,
177     kRegister,
178     kFixedRegister,
179     kFPRegister,
180     kFixedFPRegister,
181     kSlot,
182     kFixedSlot,
183     kNone,
184     kNoneFP,
185     kExplicit,
186     kSameAsFirst,
187     kRegisterAndSlot
188   };
189 
190   struct OperandConstraint {
191     ConstraintType type_;
192     // Constant or immediate value, register code, slot index, or slot size
193     // when relevant.
194     int value_;
195     int spilled_slot_;
196     int virtual_register_;
197   };
198 
199   struct InstructionConstraint {
200     const Instruction* instruction_;
201     size_t operand_constaints_size_;
202     OperandConstraint* operand_constraints_;
203   };
204 
205   typedef ZoneVector<InstructionConstraint> Constraints;
206 
207   class DelayedAssessments : public ZoneObject {
208    public:
DelayedAssessments(Zone * zone)209     explicit DelayedAssessments(Zone* zone) : map_(zone) {}
210 
map()211     const ZoneMap<InstructionOperand, int, OperandAsKeyLess>& map() const {
212       return map_;
213     }
214 
AddDelayedAssessment(InstructionOperand op,int vreg)215     void AddDelayedAssessment(InstructionOperand op, int vreg) {
216       auto it = map_.find(op);
217       if (it == map_.end()) {
218         map_.insert(std::make_pair(op, vreg));
219       } else {
220         CHECK_EQ(it->second, vreg);
221       }
222     }
223 
224    private:
225     ZoneMap<InstructionOperand, int, OperandAsKeyLess> map_;
226   };
227 
zone()228   Zone* zone() const { return zone_; }
config()229   const RegisterConfiguration* config() { return config_; }
sequence()230   const InstructionSequence* sequence() const { return sequence_; }
constraints()231   Constraints* constraints() { return &constraints_; }
232 
233   static void VerifyInput(const OperandConstraint& constraint);
234   static void VerifyTemp(const OperandConstraint& constraint);
235   static void VerifyOutput(const OperandConstraint& constraint);
236 
237   void BuildConstraint(const InstructionOperand* op,
238                        OperandConstraint* constraint);
239   void CheckConstraint(const InstructionOperand* op,
240                        const OperandConstraint* constraint);
241   BlockAssessments* CreateForBlock(const InstructionBlock* block);
242 
243   void ValidatePendingAssessment(RpoNumber block_id, InstructionOperand op,
244                                  BlockAssessments* current_assessments,
245                                  const PendingAssessment* assessment,
246                                  int virtual_register);
247   void ValidateFinalAssessment(RpoNumber block_id, InstructionOperand op,
248                                BlockAssessments* current_assessments,
249                                const FinalAssessment* assessment,
250                                int virtual_register);
251   void ValidateUse(RpoNumber block_id, BlockAssessments* current_assessments,
252                    InstructionOperand op, int virtual_register);
253 
254   Zone* const zone_;
255   const RegisterConfiguration* config_;
256   const InstructionSequence* const sequence_;
257   Constraints constraints_;
258   ZoneMap<RpoNumber, BlockAssessments*> assessments_;
259   ZoneMap<RpoNumber, DelayedAssessments*> outstanding_assessments_;
260 
261   DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorVerifier);
262 };
263 
264 }  // namespace compiler
265 }  // namespace internal
266 }  // namespace v8
267 
268 #endif
269