v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
bytecode-register-optimizer.cc
Go to the documentation of this file.
1// Copyright 2016 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
6
8
9namespace v8 {
10namespace internal {
11namespace interpreter {
12
14
16
17// kDefinitelyHasVariable means that the variable is definitely in the register.
18// kMightHaveVariable means that the variable might be in the register.
20
25
27
29
30// A class for tracking the state of a register. This class tracks
31// which equivalence set a register is a member of and also whether a
32// register is materialized in the bytecode stream.
34 public:
46 RegisterInfo(const RegisterInfo&) = delete;
48
54 bool IsInSameEquivalenceSet(RegisterInfo* info) const;
55
56 // Get a member of the register's equivalence set that is allocated.
57 // Returns itself if allocated, and nullptr if there is no unallocated
58 // equivalent register.
60
61 // Get a member of this register's equivalence set that is
62 // materialized. The materialized equivalent will be this register
63 // if it is materialized. Returns nullptr if no materialized
64 // equivalent exists.
66
67 // Get a member of this register's equivalence set that is
68 // materialized and not register |reg|. The materialized equivalent
69 // will be this register if it is materialized. Returns nullptr if
70 // no materialized equivalent exists.
72
73 // Get a member of this register's equivalence set that is intended
74 // to be materialized in place of this register (which is currently
75 // materialized). The best candidate is deemed to be the register
76 // with the lowest index as this permits temporary registers to be
77 // removed from the bytecode stream. Returns nullptr if no candidate
78 // exists.
80
81 // Marks all temporary registers of the equivalence set as unmaterialized.
82 void MarkTemporariesAsUnmaterialized(Register temporary_base);
83
84 // Get an equivalent register. Returns this if none exists.
86
87 Register register_value() const { return register_; }
88 bool materialized() const { return materialized_; }
90 bool allocated() const { return allocated_; }
91 void set_allocated(bool allocated) { allocated_ = allocated; }
95 uint32_t equivalence_id() const { return equivalence_id_; }
96 // Indicates if a register should be processed when calling Flush().
97 bool needs_flush() const { return needs_flush_; }
99 TypeHint type_hint() const { return type_hint_; }
100 void set_type_hint(TypeHint hint) { type_hint_ = hint; }
101
104 void flush_variable_hint(bool reset_variable_hint) {
105 if (reset_variable_hint) {
107 } else if (variable_hint_.variable != nullptr) {
109 }
110 }
111
112 RegisterInfo* next() const { return next_; }
113
114 private:
122
123 // Equivalence set pointers.
126};
127
129 RegisterInfo* info) {
130 DCHECK_NE(kInvalidEquivalenceId, info->equivalence_id());
131 // Fix old list
132 next_->prev_ = prev_;
133 prev_->next_ = next_;
134 // Add to new list.
135 next_ = info->next_;
136 prev_ = info;
137 prev_->next_ = this;
138 next_->prev_ = this;
139 set_equivalence_id(info->equivalence_id());
140 set_materialized(false);
141 set_variable_hint(info->variable_hint());
142 type_hint_ = info->type_hint();
143}
144
146 uint32_t equivalence_id, MaterializedInfo materialized,
147 ResetVariableHint reset) {
148 next_->prev_ = prev_;
149 prev_->next_ = next_;
150 next_ = prev_ = this;
151 equivalence_id_ = equivalence_id;
152 materialized_ = materialized == MaterializedInfo::kMaterialized;
153 flush_variable_hint(reset == ResetVariableHint::kReset);
154 type_hint_ = TypeHint::kAny;
155}
156
158 const {
159 return this->next_ == this;
160}
161
163 Register reg) {
165 RegisterInfo* it = info;
166 do {
168 it->set_variable_hint({var, VariableHintMode::kDefinitelyHasVariable});
169 it = it->next();
170 } while (it != info);
171}
172
174 Register reg) {
176 return info->variable_hint().variable;
177}
178
180 Register reg) {
181 DCHECK_NOT_NULL(var);
183 VariableHint hint = info->variable_hint();
185 hint.variable == var;
186}
187
192
200
204
206 return accumulator_info_->type_hint() == TypeHint::kAny;
207}
208
210 RegisterInfo* info) const {
211 return equivalence_id() == info->equivalence_id();
212}
213
216 RegisterInfo* visitor = this;
217 do {
218 if (visitor->allocated()) {
219 return visitor;
220 }
221 visitor = visitor->next_;
222 } while (visitor != this);
223
224 return nullptr;
225}
226
229 RegisterInfo* visitor = this;
230 do {
231 if (visitor->materialized()) {
232 return visitor;
233 }
234 visitor = visitor->next_;
235 } while (visitor != this);
236
237 return nullptr;
238}
239
242 Register reg) {
243 RegisterInfo* visitor = this;
244 do {
245 if (visitor->materialized() && visitor->register_value() != reg) {
246 return visitor;
247 }
248 visitor = visitor->next_;
249 } while (visitor != this);
250
251 return nullptr;
252}
253
256 DCHECK(this->materialized());
257 RegisterInfo* visitor = this->next_;
258 RegisterInfo* best_info = nullptr;
259 while (visitor != this) {
260 if (visitor->materialized()) {
261 return nullptr;
262 }
263 if (visitor->allocated() &&
264 (best_info == nullptr ||
265 visitor->register_value() < best_info->register_value())) {
266 best_info = visitor;
267 }
268 visitor = visitor->next_;
269 }
270 return best_info;
271}
272
274 Register temporary_base) {
275 DCHECK(this->register_value() < temporary_base);
276 DCHECK(this->materialized());
277 RegisterInfo* visitor = this->next_;
278 while (visitor != this) {
279 if (visitor->register_value() >= temporary_base) {
280 visitor->set_materialized(false);
281 }
282 visitor = visitor->next_;
283 }
284}
285
290
292 Zone* zone, BytecodeRegisterAllocator* register_allocator,
293 int fixed_registers_count, int parameter_count,
294 BytecodeWriter* bytecode_writer)
295 : accumulator_(Register::virtual_accumulator()),
296 temporary_base_(fixed_registers_count),
297 max_register_index_(fixed_registers_count - 1),
301 bytecode_writer_(bytecode_writer),
302 flush_required_(false),
303 zone_(zone) {
304 register_allocator->set_observer(this);
305
306 // Calculate offset so register index values can be mapped into
307 // a vector of register metadata.
308 // There is at least one parameter, which is the JS receiver.
310 int first_slot_index = parameter_count - 1;
312 -Register::FromParameterIndex(first_slot_index).index();
313
314 // Initialize register map for parameters, locals, and the
315 // accumulator.
317 static_cast<size_t>(temporary_base_.index()));
318 for (size_t i = 0; i < register_info_table_.size(); ++i) {
321 DCHECK_EQ(register_info_table_[i]->register_value().index(),
323 }
326}
327
329 // Flushing is required in two cases:
330 // 1) Two or more registers in the same equivalence set.
331 // 2) Binding a variable to a register.
332 flush_required_ = true;
333 if (!reg->needs_flush()) {
334 reg->set_needs_flush(true);
336 }
337}
338
340 for (RegisterInfo* reg_info : register_info_table_) {
341 if (reg_info->needs_flush()) {
342 return false;
343 } else if (!reg_info->IsOnlyMemberOfEquivalenceSet()) {
344 return false;
345 } else if (reg_info->allocated() && !reg_info->materialized()) {
346 return false;
347 }
348 }
349 return true;
350}
351
353 if (!flush_required_) {
354 return;
355 }
356
357 // Materialize all live registers and break equivalences.
358 for (RegisterInfo* reg_info : registers_needing_flushed_) {
359 if (!reg_info->needs_flush()) continue;
360 reg_info->set_needs_flush(false);
361 reg_info->flush_variable_hint(false);
362 reg_info->set_type_hint(TypeHint::kAny);
363
364 RegisterInfo* materialized = reg_info->materialized()
365 ? reg_info
366 : reg_info->GetMaterializedEquivalent();
367
368 if (materialized != nullptr) {
369 // Walk equivalents of materialized registers, materializing
370 // each equivalent register as necessary and placing in their
371 // own equivalence set.
372 RegisterInfo* equivalent;
373 while ((equivalent = materialized->GetEquivalent()) != materialized) {
374 if (equivalent->allocated() && !equivalent->materialized()) {
375 OutputRegisterTransfer(materialized, equivalent);
376 }
380 equivalent->set_needs_flush(false);
381 }
382 } else {
383 // Equivalence class containing only unallocated registers.
384 DCHECK_NULL(reg_info->GetAllocatedEquivalent());
385 reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(),
388 }
389 }
390
393
394 flush_required_ = false;
395}
396
398 RegisterInfo* input_info, RegisterInfo* output_info) {
399 Register input = input_info->register_value();
400 Register output = output_info->register_value();
401 DCHECK_NE(input.index(), output.index());
402
403 if (input == accumulator_) {
404 bytecode_writer_->EmitStar(output);
405 } else if (output == accumulator_) {
407 } else {
408 bytecode_writer_->EmitMov(input, output);
409 }
410 if (output != accumulator_) {
411 max_register_index_ = std::max(max_register_index_, output.index());
412 }
413 output_info->set_materialized(true);
414}
415
417 RegisterInfo* info) {
418 DCHECK(info->materialized());
419 RegisterInfo* unmaterialized = info->GetEquivalentToMaterialize();
420 if (unmaterialized) {
421 OutputRegisterTransfer(info, unmaterialized);
422 }
423}
424
427 RegisterInfo* info) {
428 if (info->materialized()) {
429 return info;
430 }
431
433 if (result == nullptr) {
434 Materialize(info);
435 result = info;
436 }
437 DCHECK(result->register_value() != accumulator_);
438 return result;
439}
440
442 if (!info->materialized()) {
443 RegisterInfo* materialized = info->GetMaterializedEquivalent();
444 DCHECK_NOT_NULL(materialized);
445 OutputRegisterTransfer(materialized, info);
446 }
447}
448
450 RegisterInfo* set_member, RegisterInfo* non_set_member) {
451 // Equivalence class is now of size >= 2, so we make sure it will be flushed.
452 PushToRegistersNeedingFlush(non_set_member);
453 non_set_member->AddToEquivalenceSetOf(set_member);
454}
455
457 RegisterInfo* output_info) {
458 bool output_is_observable =
459 RegisterIsObservable(output_info->register_value());
460 bool in_same_equivalence_set =
461 output_info->IsInSameEquivalenceSet(input_info);
462 if (in_same_equivalence_set &&
463 (!output_is_observable || output_info->materialized())) {
464 return; // Nothing more to do.
465 }
466
467 // Materialize an alternate in the equivalence set that
468 // |output_info| is leaving.
469 if (output_info->materialized()) {
470 CreateMaterializedEquivalent(output_info);
471 }
472
473 // Add |output_info| to new equivalence set.
474 if (!in_same_equivalence_set) {
475 AddToEquivalenceSet(input_info, output_info);
476 }
477
478 if (output_is_observable) {
479 // Force store to be emitted when register is observable.
480 output_info->set_materialized(false);
481 RegisterInfo* materialized_info = input_info->GetMaterializedEquivalent();
482 OutputRegisterTransfer(materialized_info, output_info);
483 }
484
485 bool input_is_observable = RegisterIsObservable(input_info->register_value());
486 if (input_is_observable) {
487 // If input is observable by the debugger, mark all other temporaries
488 // registers as unmaterialized so that this register is used in preference.
490 }
491}
492
503
505 RegisterList reg_list) {
506 int start_index = reg_list.first_register().index();
507 for (int i = 0; i < reg_list.register_count(); ++i) {
508 Register current(start_index + i);
509 PrepareOutputRegister(current);
510 }
511}
512
514 RegisterInfo* reg_info = GetRegisterInfo(reg);
515 if (reg_info->materialized()) {
516 return reg;
517 } else {
518 RegisterInfo* equivalent_info =
520 return equivalent_info->register_value();
521 }
522}
523
525 RegisterList reg_list) {
526 if (reg_list.register_count() == 1) {
527 // If there is only a single register, treat it as a normal input register.
529 return RegisterList(reg);
530 } else {
531 int start_index = reg_list.first_register().index();
532 for (int i = 0; i < reg_list.register_count(); ++i) {
533 Register current(start_index + i);
534 RegisterInfo* input_info = GetRegisterInfo(current);
535 Materialize(input_info);
536 }
537 return reg_list;
538 }
539}
540
543 size_t index = GetRegisterInfoTableIndex(reg);
544 if (index >= register_info_table_.size()) {
545 size_t new_size = index + 1;
546 size_t old_size = register_info_table_.size();
547 register_info_table_.resize(new_size);
548 for (size_t i = old_size; i < new_size; ++i) {
551 NextEquivalenceId(), true, false);
552 }
553 }
554}
555
557 info->set_allocated(true);
558 if (!info->materialized()) {
559 info->MoveToNewEquivalenceSet(NextEquivalenceId(),
561 }
562}
563
567
569 RegisterList reg_list) {
570 if (reg_list.register_count() != 0) {
571 int first_index = reg_list.first_register().index();
572 GrowRegisterMap(Register(first_index + reg_list.register_count() - 1));
573 for (int i = 0; i < reg_list.register_count(); i++) {
575 }
576 }
577}
578
580 int first_index = reg_list.first_register().index();
581 for (int i = 0; i < reg_list.register_count(); i++) {
582 GetRegisterInfo(Register(first_index + i))->set_allocated(false);
583 }
584}
585
589
590} // namespace interpreter
591} // namespace internal
592} // namespace v8
int16_t parameter_count
Definition builtins.cc:67
T * New(Args &&... args)
Definition zone.h:114
static bool IsSameOrSubTypeHint(TypeHint hint1, TypeHint hint2)
virtual void EmitMov(Register input, Register output)=0
void MoveToNewEquivalenceSet(uint32_t equivalence_id, MaterializedInfo materialized, ResetVariableHint reset=ResetVariableHint::kReset)
RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized, bool allocated)
RegisterInfo & operator=(const RegisterInfo &)=delete
void RegisterTransfer(RegisterInfo *input, RegisterInfo *output)
void AddToEquivalenceSet(RegisterInfo *set_member, RegisterInfo *non_set_member)
void OutputRegisterTransfer(RegisterInfo *input, RegisterInfo *output)
RegisterInfo * GetMaterializedEquivalentNotAccumulator(RegisterInfo *info)
BytecodeRegisterOptimizer(Zone *zone, BytecodeRegisterAllocator *register_allocator, int fixed_registers_count, int parameter_count, BytecodeWriter *bytecode_writer)
static constexpr Register FromParameterIndex(int index)
Handle< SharedFunctionInfo > info
LineAndColumn current
ZoneVector< RpoNumber > & result
LiftoffRegister reg
constexpr uint32_t kMaxUInt32
Definition globals.h:387
Node * prev_
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485