Home | History | Annotate | Download | only in compiler
      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:
     56   AssessmentKind kind() const { return kind_; }
     57 
     58  protected:
     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:
     74   explicit PendingAssessment(const InstructionBlock* origin,
     75                              InstructionOperand operand)
     76       : Assessment(Pending), origin_(origin), operand_(operand) {}
     77 
     78   static const PendingAssessment* cast(const Assessment* assessment) {
     79     CHECK(assessment->kind() == Pending);
     80     return static_cast<const PendingAssessment*>(assessment);
     81   }
     82 
     83   const InstructionBlock* origin() const { return origin_; }
     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)
     99       : Assessment(Final),
    100         virtual_register_(virtual_register),
    101         original_pending_assessment_(original_pending) {}
    102 
    103   int virtual_register() const { return virtual_register_; }
    104   static const FinalAssessment* cast(const Assessment* assessment) {
    105     CHECK(assessment->kind() == Final);
    106     return static_cast<const FinalAssessment*>(assessment);
    107   }
    108 
    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 {
    121   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;
    131   explicit BlockAssessments(Zone* zone)
    132       : map_(zone), map_for_moves_(zone), zone_(zone) {}
    133   void Drop(InstructionOperand operand) { map_.erase(operand); }
    134   void DropRegisters();
    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);
    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 
    153   OperandMap& map() { return 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:
    209     explicit DelayedAssessments(Zone* zone) : map_(zone) {}
    210 
    211     const ZoneMap<InstructionOperand, int, OperandAsKeyLess>& map() const {
    212       return map_;
    213     }
    214 
    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 
    228   Zone* zone() const { return zone_; }
    229   const RegisterConfiguration* config() { return config_; }
    230   const InstructionSequence* sequence() const { return sequence_; }
    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