v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
register-allocator-verifier.h
Go to the documentation of this file.
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_BACKEND_REGISTER_ALLOCATOR_VERIFIER_H_
6#define V8_COMPILER_BACKEND_REGISTER_ALLOCATOR_VERIFIER_H_
7
8#include <optional>
9
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
17class InstructionBlock;
18class InstructionSequence;
19
20// The register allocator validator traverses instructions in the instruction
21// sequence, and verifies the correctness of machine operand substitutions of
22// virtual registers. It collects the virtual register instruction signatures
23// before register allocation. Then, after the register allocation pipeline
24// completes, it compares the operand substitutions against the pre-allocation
25// data.
26// At a high level, validation works as follows: we iterate through each block,
27// and, in a block, through each instruction; then:
28// - when an operand is the output of an instruction, we associate it to the
29// virtual register that the instruction sequence declares as its output. We
30// use the concept of "FinalAssessment" to model this.
31// - when an operand is used in an instruction, we check that the assessment
32// matches the expectation of the instruction
33// - moves simply copy the assessment over to the new operand
34// - blocks with more than one predecessor associate to each operand a "Pending"
35// assessment. The pending assessment remembers the operand and block where it
36// was created. Then, when the value is used (which may be as a different
37// operand, because of moves), we check that the virtual register at the use
38// site matches the definition of this pending operand: either the phi inputs
39// match, or, if it's not a phi, all the predecessors at the point the pending
40// assessment was defined have that operand assigned to the given virtual
41// register. If all checks out, we record in the assessment that the virtual
42// register is aliased by the specific operand.
43// If a block is a loop header - so one or more of its predecessors are it or
44// below - we still treat uses of operands as above, but we record which operand
45// assessments haven't been made yet, and what virtual register they must
46// correspond to, and verify that when we are done with the respective
47// predecessor blocks.
48// This way, the algorithm always makes a final decision about the operands
49// in an instruction, ensuring convergence.
50// Operand assessments are recorded per block, as the result at the exit from
51// the block. When moving to a new block, we copy assessments from its single
52// predecessor, or, if the block has multiple predecessors, the mechanism was
53// described already.
54
56
57class Assessment : public ZoneObject {
58 public:
59 Assessment(const Assessment&) = delete;
60 Assessment& operator=(const Assessment&) = delete;
61
62 AssessmentKind kind() const { return kind_; }
63
64 protected:
67};
68
69// PendingAssessments are associated to operands coming from the multiple
70// predecessors of a block. We only record the operand and the block, and
71// will determine if the way the operand is defined (from the predecessors)
72// matches a particular use. We allow more than one vreg association with
73// an operand - this handles scenarios where multiple phis are
74// defined with identical operands, and the move optimizer moved down the moves
75// separating the 2 phis in the block defining them.
76class PendingAssessment final : public Assessment {
77 public:
84
87
88 static const PendingAssessment* cast(const Assessment* assessment) {
89 CHECK(assessment->kind() == Pending);
90 return static_cast<const PendingAssessment*>(assessment);
91 }
92
93 static PendingAssessment* cast(Assessment* assessment) {
94 CHECK(assessment->kind() == Pending);
95 return static_cast<PendingAssessment*>(assessment);
96 }
97
98 const InstructionBlock* origin() const { return origin_; }
100 bool IsAliasOf(int vreg) const { return aliases_.count(vreg) > 0; }
101 void AddAlias(int vreg) { aliases_.insert(vreg); }
102
103 private:
107};
108
109// FinalAssessments are associated to operands that we know to be a certain
110// virtual register.
111class FinalAssessment final : public Assessment {
112 public:
117
118 int virtual_register() const { return virtual_register_; }
119 static const FinalAssessment* cast(const Assessment* assessment) {
120 CHECK(assessment->kind() == Final);
121 return static_cast<const FinalAssessment*>(assessment);
122 }
123
124 private:
126};
127
130 const InstructionOperand& b) const {
131 return a.CompareCanonicalized(b);
132 }
133};
134
135// Assessments associated with a basic block.
137 public:
141 const InstructionSequence* sequence)
142 : map_(zone),
143 map_for_moves_(zone),
146 zone_(zone),
147 sequence_(sequence) {}
150
151 void Drop(InstructionOperand operand) {
152 map_.erase(operand);
153 stale_ref_stack_slots_.erase(operand);
154 }
155 void DropRegisters();
156 void AddDefinition(InstructionOperand operand, int virtual_register) {
157 auto existent = map_.find(operand);
158 if (existent != map_.end()) {
159 // Drop the assignment
160 map_.erase(existent);
161 // Destination operand is no longer a stale reference.
162 stale_ref_stack_slots_.erase(operand);
163 }
164 map_.insert(
165 std::make_pair(operand, zone_->New<FinalAssessment>(virtual_register)));
166 }
167
168 void PerformMoves(const Instruction* instruction);
169 void PerformParallelMoves(const ParallelMove* moves);
170 void CopyFrom(const BlockAssessments* other) {
171 CHECK(map_.empty());
173 CHECK_NOT_NULL(other);
174 map_.insert(other->map_.begin(), other->map_.end());
175 stale_ref_stack_slots_.insert(other->stale_ref_stack_slots_.begin(),
176 other->stale_ref_stack_slots_.end());
177 }
178 void CheckReferenceMap(const ReferenceMap* reference_map);
180 std::optional<int> vreg = std::nullopt);
181
182 OperandMap& map() { return map_; }
183 const OperandMap& map() const { return map_; }
184
188 }
189
190 int spill_slot_delta() const { return spill_slot_delta_; }
191
192 void Print() const;
193
194 private:
197 // TODOC(dmercadier): how do stack slots become stale exactly? What are the
198 // implications of a stack slot being stale?
203};
204
206 public:
208 const InstructionSequence* sequence,
209 const Frame* frame);
212 delete;
213
214 void VerifyAssignment(const char* caller_info);
215 void VerifyGapMoves();
216
217 private:
233
236 // Constant or immediate value, register code, slot index, or slot size
237 // when relevant.
241 };
242
248
250
252 public:
254
258
260 auto it = map_.find(op);
261 if (it == map_.end()) {
262 map_.insert(std::make_pair(op, vreg));
263 } else {
264 CHECK_EQ(it->second, vreg);
265 }
266 }
267
268 private:
270 };
271
272 Zone* zone() const { return zone_; }
274 const InstructionSequence* sequence() const { return sequence_; }
276 int spill_slot_delta() const { return spill_slot_delta_; }
277
278 static void VerifyInput(const OperandConstraint& constraint);
279 static void VerifyTemp(const OperandConstraint& constraint);
280 static void VerifyOutput(const OperandConstraint& constraint);
281
282 void BuildConstraint(const InstructionOperand* op,
283 OperandConstraint* constraint);
284 void CheckConstraint(const InstructionOperand* op,
285 const OperandConstraint* constraint);
287
288 // Prove that this operand is an alias of this virtual register in the given
289 // block. Update the assessment if that's the case.
291 const BlockAssessments* current_assessments,
292 PendingAssessment* const assessment,
293 int virtual_register);
294 void ValidateUse(RpoNumber block_id, BlockAssessments* current_assessments,
295 InstructionOperand op, int virtual_register);
296
297 Zone* const zone_;
304 // TODO(chromium:725559): remove after we understand this bug's root cause.
305 const char* caller_info_ = nullptr;
306};
307
308} // namespace compiler
309} // namespace internal
310} // namespace v8
311
312#endif // V8_COMPILER_BACKEND_REGISTER_ALLOCATOR_VERIFIER_H_
T * New(Args &&... args)
Definition zone.h:114
Assessment & operator=(const Assessment &)=delete
Assessment(const Assessment &)=delete
void CheckReferenceMap(const ReferenceMap *reference_map)
bool IsStaleReferenceStackSlot(InstructionOperand op, std::optional< int > vreg=std::nullopt)
BlockAssessments(Zone *zone, int spill_slot_delta, const InstructionSequence *sequence)
void PerformMoves(const Instruction *instruction)
void AddDefinition(InstructionOperand operand, int virtual_register)
BlockAssessments(const BlockAssessments &)=delete
BlockAssessments & operator=(const BlockAssessments &)=delete
FinalAssessment & operator=(const FinalAssessment &)=delete
static const FinalAssessment * cast(const Assessment *assessment)
FinalAssessment(const FinalAssessment &)=delete
PendingAssessment(const PendingAssessment &)=delete
static const PendingAssessment * cast(const Assessment *assessment)
PendingAssessment(Zone *zone, const InstructionBlock *origin, InstructionOperand operand)
PendingAssessment & operator=(const PendingAssessment &)=delete
static PendingAssessment * cast(Assessment *assessment)
const ZoneMap< InstructionOperand, int, OperandAsKeyLess > & map() const
RegisterAllocatorVerifier(const RegisterAllocatorVerifier &)=delete
static void VerifyOutput(const OperandConstraint &constraint)
BlockAssessments * CreateForBlock(const InstructionBlock *block)
ZoneMap< RpoNumber, BlockAssessments * > assessments_
static void VerifyInput(const OperandConstraint &constraint)
RegisterAllocatorVerifier(Zone *zone, const RegisterConfiguration *config, const InstructionSequence *sequence, const Frame *frame)
static void VerifyTemp(const OperandConstraint &constraint)
void ValidateUse(RpoNumber block_id, BlockAssessments *current_assessments, InstructionOperand op, int virtual_register)
RegisterAllocatorVerifier & operator=(const RegisterAllocatorVerifier &)=delete
void BuildConstraint(const InstructionOperand *op, OperandConstraint *constraint)
void ValidatePendingAssessment(RpoNumber block_id, InstructionOperand op, const BlockAssessments *current_assessments, PendingAssessment *const assessment, int virtual_register)
ZoneMap< RpoNumber, DelayedAssessments * > outstanding_assessments_
void CheckConstraint(const InstructionOperand *op, const OperandConstraint *constraint)
#define CHECK(condition)
Definition logging.h:124
#define CHECK_NOT_NULL(val)
#define CHECK_EQ(lhs, rhs)
bool operator()(const InstructionOperand &a, const InstructionOperand &b) const