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
5 #include "src/interpreter/bytecode-register-optimizer.h"
6
7 namespace v8 {
8 namespace internal {
9 namespace interpreter {
10
11 const uint32_t BytecodeRegisterOptimizer::kInvalidEquivalenceId = kMaxUInt32;
12
13 // A class for tracking the state of a register. This class tracks
14 // which equivalence set a register is a member of and also whether a
15 // register is materialized in the bytecode stream.
16 class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
17 public:
RegisterInfo(Register reg,uint32_t equivalence_id,bool materialized,bool allocated)18 RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized,
19 bool allocated)
20 : register_(reg),
21 equivalence_id_(equivalence_id),
22 materialized_(materialized),
23 allocated_(allocated),
24 needs_flush_(false),
25 next_(this),
26 prev_(this) {}
27 RegisterInfo(const RegisterInfo&) = delete;
28 RegisterInfo& operator=(const RegisterInfo&) = delete;
29
30 void AddToEquivalenceSetOf(RegisterInfo* info);
31 void MoveToNewEquivalenceSet(uint32_t equivalence_id, bool materialized);
32 bool IsOnlyMemberOfEquivalenceSet() const;
33 bool IsOnlyMaterializedMemberOfEquivalenceSet() const;
34 bool IsInSameEquivalenceSet(RegisterInfo* info) const;
35
36 // Get a member of the register's equivalence set that is allocated.
37 // Returns itself if allocated, and nullptr if there is no unallocated
38 // equivalent register.
39 RegisterInfo* GetAllocatedEquivalent();
40
41 // Get a member of this register's equivalence set that is
42 // materialized. The materialized equivalent will be this register
43 // if it is materialized. Returns nullptr if no materialized
44 // equivalent exists.
45 RegisterInfo* GetMaterializedEquivalent();
46
47 // Get a member of this register's equivalence set that is
48 // materialized and not register |reg|. The materialized equivalent
49 // will be this register if it is materialized. Returns nullptr if
50 // no materialized equivalent exists.
51 RegisterInfo* GetMaterializedEquivalentOtherThan(Register reg);
52
53 // Get a member of this register's equivalence set that is intended
54 // to be materialized in place of this register (which is currently
55 // materialized). The best candidate is deemed to be the register
56 // with the lowest index as this permits temporary registers to be
57 // removed from the bytecode stream. Returns nullptr if no candidate
58 // exists.
59 RegisterInfo* GetEquivalentToMaterialize();
60
61 // Marks all temporary registers of the equivalence set as unmaterialized.
62 void MarkTemporariesAsUnmaterialized(Register temporary_base);
63
64 // Get an equivalent register. Returns this if none exists.
65 RegisterInfo* GetEquivalent();
66
register_value() const67 Register register_value() const { return register_; }
materialized() const68 bool materialized() const { return materialized_; }
set_materialized(bool materialized)69 void set_materialized(bool materialized) { materialized_ = materialized; }
allocated() const70 bool allocated() const { return allocated_; }
set_allocated(bool allocated)71 void set_allocated(bool allocated) { allocated_ = allocated; }
set_equivalence_id(uint32_t equivalence_id)72 void set_equivalence_id(uint32_t equivalence_id) {
73 equivalence_id_ = equivalence_id;
74 }
equivalence_id() const75 uint32_t equivalence_id() const { return equivalence_id_; }
76 // Indicates if a register should be processed when calling Flush().
needs_flush() const77 bool needs_flush() const { return needs_flush_; }
set_needs_flush(bool needs_flush)78 void set_needs_flush(bool needs_flush) { needs_flush_ = needs_flush; }
79
80 private:
81 Register register_;
82 uint32_t equivalence_id_;
83 bool materialized_;
84 bool allocated_;
85 bool needs_flush_;
86
87 // Equivalence set pointers.
88 RegisterInfo* next_;
89 RegisterInfo* prev_;
90 };
91
AddToEquivalenceSetOf(RegisterInfo * info)92 void BytecodeRegisterOptimizer::RegisterInfo::AddToEquivalenceSetOf(
93 RegisterInfo* info) {
94 DCHECK_NE(kInvalidEquivalenceId, info->equivalence_id());
95 // Fix old list
96 next_->prev_ = prev_;
97 prev_->next_ = next_;
98 // Add to new list.
99 next_ = info->next_;
100 prev_ = info;
101 prev_->next_ = this;
102 next_->prev_ = this;
103 set_equivalence_id(info->equivalence_id());
104 set_materialized(false);
105 }
106
MoveToNewEquivalenceSet(uint32_t equivalence_id,bool materialized)107 void BytecodeRegisterOptimizer::RegisterInfo::MoveToNewEquivalenceSet(
108 uint32_t equivalence_id, bool materialized) {
109 next_->prev_ = prev_;
110 prev_->next_ = next_;
111 next_ = prev_ = this;
112 equivalence_id_ = equivalence_id;
113 materialized_ = materialized;
114 }
115
IsOnlyMemberOfEquivalenceSet() const116 bool BytecodeRegisterOptimizer::RegisterInfo::IsOnlyMemberOfEquivalenceSet()
117 const {
118 return this->next_ == this;
119 }
120
121 bool BytecodeRegisterOptimizer::RegisterInfo::
IsOnlyMaterializedMemberOfEquivalenceSet() const122 IsOnlyMaterializedMemberOfEquivalenceSet() const {
123 DCHECK(materialized());
124
125 const RegisterInfo* visitor = this->next_;
126 while (visitor != this) {
127 if (visitor->materialized()) {
128 return false;
129 }
130 visitor = visitor->next_;
131 }
132 return true;
133 }
134
IsInSameEquivalenceSet(RegisterInfo * info) const135 bool BytecodeRegisterOptimizer::RegisterInfo::IsInSameEquivalenceSet(
136 RegisterInfo* info) const {
137 return equivalence_id() == info->equivalence_id();
138 }
139
140 BytecodeRegisterOptimizer::RegisterInfo*
GetAllocatedEquivalent()141 BytecodeRegisterOptimizer::RegisterInfo::GetAllocatedEquivalent() {
142 RegisterInfo* visitor = this;
143 do {
144 if (visitor->allocated()) {
145 return visitor;
146 }
147 visitor = visitor->next_;
148 } while (visitor != this);
149
150 return nullptr;
151 }
152
153 BytecodeRegisterOptimizer::RegisterInfo*
GetMaterializedEquivalent()154 BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalent() {
155 RegisterInfo* visitor = this;
156 do {
157 if (visitor->materialized()) {
158 return visitor;
159 }
160 visitor = visitor->next_;
161 } while (visitor != this);
162
163 return nullptr;
164 }
165
166 BytecodeRegisterOptimizer::RegisterInfo*
GetMaterializedEquivalentOtherThan(Register reg)167 BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalentOtherThan(
168 Register reg) {
169 RegisterInfo* visitor = this;
170 do {
171 if (visitor->materialized() && visitor->register_value() != reg) {
172 return visitor;
173 }
174 visitor = visitor->next_;
175 } while (visitor != this);
176
177 return nullptr;
178 }
179
180 BytecodeRegisterOptimizer::RegisterInfo*
GetEquivalentToMaterialize()181 BytecodeRegisterOptimizer::RegisterInfo::GetEquivalentToMaterialize() {
182 DCHECK(this->materialized());
183 RegisterInfo* visitor = this->next_;
184 RegisterInfo* best_info = nullptr;
185 while (visitor != this) {
186 if (visitor->materialized()) {
187 return nullptr;
188 }
189 if (visitor->allocated() &&
190 (best_info == nullptr ||
191 visitor->register_value() < best_info->register_value())) {
192 best_info = visitor;
193 }
194 visitor = visitor->next_;
195 }
196 return best_info;
197 }
198
MarkTemporariesAsUnmaterialized(Register temporary_base)199 void BytecodeRegisterOptimizer::RegisterInfo::MarkTemporariesAsUnmaterialized(
200 Register temporary_base) {
201 DCHECK(this->register_value() < temporary_base);
202 DCHECK(this->materialized());
203 RegisterInfo* visitor = this->next_;
204 while (visitor != this) {
205 if (visitor->register_value() >= temporary_base) {
206 visitor->set_materialized(false);
207 }
208 visitor = visitor->next_;
209 }
210 }
211
212 BytecodeRegisterOptimizer::RegisterInfo*
GetEquivalent()213 BytecodeRegisterOptimizer::RegisterInfo::GetEquivalent() {
214 return next_;
215 }
216
BytecodeRegisterOptimizer(Zone * zone,BytecodeRegisterAllocator * register_allocator,int fixed_registers_count,int parameter_count,BytecodeWriter * bytecode_writer)217 BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
218 Zone* zone, BytecodeRegisterAllocator* register_allocator,
219 int fixed_registers_count, int parameter_count,
220 BytecodeWriter* bytecode_writer)
221 : accumulator_(Register::virtual_accumulator()),
222 temporary_base_(fixed_registers_count),
223 max_register_index_(fixed_registers_count - 1),
224 register_info_table_(zone),
225 registers_needing_flushed_(zone),
226 equivalence_id_(0),
227 bytecode_writer_(bytecode_writer),
228 flush_required_(false),
229 zone_(zone) {
230 register_allocator->set_observer(this);
231
232 // Calculate offset so register index values can be mapped into
233 // a vector of register metadata.
234 // There is at least one parameter, which is the JS receiver.
235 DCHECK_NE(parameter_count, 0);
236 int first_slot_index = parameter_count - 1;
237 register_info_table_offset_ =
238 -Register::FromParameterIndex(first_slot_index, parameter_count).index();
239
240 // Initialize register map for parameters, locals, and the
241 // accumulator.
242 register_info_table_.resize(register_info_table_offset_ +
243 static_cast<size_t>(temporary_base_.index()));
244 for (size_t i = 0; i < register_info_table_.size(); ++i) {
245 register_info_table_[i] = zone->New<RegisterInfo>(
246 RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), true, true);
247 DCHECK_EQ(register_info_table_[i]->register_value().index(),
248 RegisterFromRegisterInfoTableIndex(i).index());
249 }
250 accumulator_info_ = GetRegisterInfo(accumulator_);
251 DCHECK(accumulator_info_->register_value() == accumulator_);
252 }
253
PushToRegistersNeedingFlush(RegisterInfo * reg)254 void BytecodeRegisterOptimizer::PushToRegistersNeedingFlush(RegisterInfo* reg) {
255 if (!reg->needs_flush()) {
256 reg->set_needs_flush(true);
257 registers_needing_flushed_.push_back(reg);
258 }
259 }
260
EnsureAllRegistersAreFlushed() const261 bool BytecodeRegisterOptimizer::EnsureAllRegistersAreFlushed() const {
262 for (RegisterInfo* reg_info : register_info_table_) {
263 if (reg_info->needs_flush()) {
264 return false;
265 } else if (!reg_info->IsOnlyMemberOfEquivalenceSet()) {
266 return false;
267 } else if (reg_info->allocated() && !reg_info->materialized()) {
268 return false;
269 }
270 }
271 return true;
272 }
273
Flush()274 void BytecodeRegisterOptimizer::Flush() {
275 if (!flush_required_) {
276 return;
277 }
278
279 // Materialize all live registers and break equivalences.
280 for (RegisterInfo* reg_info : registers_needing_flushed_) {
281 if (!reg_info->needs_flush()) continue;
282 reg_info->set_needs_flush(false);
283
284 RegisterInfo* materialized = reg_info->materialized()
285 ? reg_info
286 : reg_info->GetMaterializedEquivalent();
287
288 if (materialized != nullptr) {
289 // Walk equivalents of materialized registers, materializing
290 // each equivalent register as necessary and placing in their
291 // own equivalence set.
292 RegisterInfo* equivalent;
293 while ((equivalent = materialized->GetEquivalent()) != materialized) {
294 if (equivalent->allocated() && !equivalent->materialized()) {
295 OutputRegisterTransfer(materialized, equivalent);
296 }
297 equivalent->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
298 equivalent->set_needs_flush(false);
299 }
300 } else {
301 // Equivalernce class containing only unallocated registers.
302 DCHECK_NULL(reg_info->GetAllocatedEquivalent());
303 reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(), false);
304 }
305 }
306
307 registers_needing_flushed_.clear();
308 DCHECK(EnsureAllRegistersAreFlushed());
309
310 flush_required_ = false;
311 }
312
OutputRegisterTransfer(RegisterInfo * input_info,RegisterInfo * output_info)313 void BytecodeRegisterOptimizer::OutputRegisterTransfer(
314 RegisterInfo* input_info, RegisterInfo* output_info) {
315 Register input = input_info->register_value();
316 Register output = output_info->register_value();
317 DCHECK_NE(input.index(), output.index());
318
319 if (input == accumulator_) {
320 bytecode_writer_->EmitStar(output);
321 } else if (output == accumulator_) {
322 bytecode_writer_->EmitLdar(input);
323 } else {
324 bytecode_writer_->EmitMov(input, output);
325 }
326 if (output != accumulator_) {
327 max_register_index_ = std::max(max_register_index_, output.index());
328 }
329 output_info->set_materialized(true);
330 }
331
CreateMaterializedEquivalent(RegisterInfo * info)332 void BytecodeRegisterOptimizer::CreateMaterializedEquivalent(
333 RegisterInfo* info) {
334 DCHECK(info->materialized());
335 RegisterInfo* unmaterialized = info->GetEquivalentToMaterialize();
336 if (unmaterialized) {
337 OutputRegisterTransfer(info, unmaterialized);
338 }
339 }
340
341 BytecodeRegisterOptimizer::RegisterInfo*
GetMaterializedEquivalent(RegisterInfo * info)342 BytecodeRegisterOptimizer::GetMaterializedEquivalent(RegisterInfo* info) {
343 return info->materialized() ? info : info->GetMaterializedEquivalent();
344 }
345
346 BytecodeRegisterOptimizer::RegisterInfo*
GetMaterializedEquivalentNotAccumulator(RegisterInfo * info)347 BytecodeRegisterOptimizer::GetMaterializedEquivalentNotAccumulator(
348 RegisterInfo* info) {
349 if (info->materialized()) {
350 return info;
351 }
352
353 RegisterInfo* result = info->GetMaterializedEquivalentOtherThan(accumulator_);
354 if (result == nullptr) {
355 Materialize(info);
356 result = info;
357 }
358 DCHECK(result->register_value() != accumulator_);
359 return result;
360 }
361
Materialize(RegisterInfo * info)362 void BytecodeRegisterOptimizer::Materialize(RegisterInfo* info) {
363 if (!info->materialized()) {
364 RegisterInfo* materialized = info->GetMaterializedEquivalent();
365 DCHECK_NOT_NULL(materialized);
366 OutputRegisterTransfer(materialized, info);
367 }
368 }
369
AddToEquivalenceSet(RegisterInfo * set_member,RegisterInfo * non_set_member)370 void BytecodeRegisterOptimizer::AddToEquivalenceSet(
371 RegisterInfo* set_member, RegisterInfo* non_set_member) {
372 // Equivalence class is now of size >= 2, so we make sure it will be flushed.
373 PushToRegistersNeedingFlush(non_set_member);
374 non_set_member->AddToEquivalenceSetOf(set_member);
375 // Flushing is only required when two or more registers are placed
376 // in the same equivalence set.
377 flush_required_ = true;
378 }
379
RegisterTransfer(RegisterInfo * input_info,RegisterInfo * output_info)380 void BytecodeRegisterOptimizer::RegisterTransfer(RegisterInfo* input_info,
381 RegisterInfo* output_info) {
382 bool output_is_observable =
383 RegisterIsObservable(output_info->register_value());
384 bool in_same_equivalence_set =
385 output_info->IsInSameEquivalenceSet(input_info);
386 if (in_same_equivalence_set &&
387 (!output_is_observable || output_info->materialized())) {
388 return; // Nothing more to do.
389 }
390
391 // Materialize an alternate in the equivalence set that
392 // |output_info| is leaving.
393 if (output_info->materialized()) {
394 CreateMaterializedEquivalent(output_info);
395 }
396
397 // Add |output_info| to new equivalence set.
398 if (!in_same_equivalence_set) {
399 AddToEquivalenceSet(input_info, output_info);
400 }
401
402 if (output_is_observable) {
403 // Force store to be emitted when register is observable.
404 output_info->set_materialized(false);
405 RegisterInfo* materialized_info = input_info->GetMaterializedEquivalent();
406 OutputRegisterTransfer(materialized_info, output_info);
407 }
408
409 bool input_is_observable = RegisterIsObservable(input_info->register_value());
410 if (input_is_observable) {
411 // If input is observable by the debugger, mark all other temporaries
412 // registers as unmaterialized so that this register is used in preference.
413 input_info->MarkTemporariesAsUnmaterialized(temporary_base_);
414 }
415 }
416
PrepareOutputRegister(Register reg)417 void BytecodeRegisterOptimizer::PrepareOutputRegister(Register reg) {
418 RegisterInfo* reg_info = GetRegisterInfo(reg);
419 if (reg_info->materialized()) {
420 CreateMaterializedEquivalent(reg_info);
421 }
422 reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
423 max_register_index_ =
424 std::max(max_register_index_, reg_info->register_value().index());
425 }
426
PrepareOutputRegisterList(RegisterList reg_list)427 void BytecodeRegisterOptimizer::PrepareOutputRegisterList(
428 RegisterList reg_list) {
429 int start_index = reg_list.first_register().index();
430 for (int i = 0; i < reg_list.register_count(); ++i) {
431 Register current(start_index + i);
432 PrepareOutputRegister(current);
433 }
434 }
435
GetInputRegister(Register reg)436 Register BytecodeRegisterOptimizer::GetInputRegister(Register reg) {
437 RegisterInfo* reg_info = GetRegisterInfo(reg);
438 if (reg_info->materialized()) {
439 return reg;
440 } else {
441 RegisterInfo* equivalent_info =
442 GetMaterializedEquivalentNotAccumulator(reg_info);
443 return equivalent_info->register_value();
444 }
445 }
446
GetInputRegisterList(RegisterList reg_list)447 RegisterList BytecodeRegisterOptimizer::GetInputRegisterList(
448 RegisterList reg_list) {
449 if (reg_list.register_count() == 1) {
450 // If there is only a single register, treat it as a normal input register.
451 Register reg(GetInputRegister(reg_list.first_register()));
452 return RegisterList(reg);
453 } else {
454 int start_index = reg_list.first_register().index();
455 for (int i = 0; i < reg_list.register_count(); ++i) {
456 Register current(start_index + i);
457 RegisterInfo* input_info = GetRegisterInfo(current);
458 Materialize(input_info);
459 }
460 return reg_list;
461 }
462 }
463
GrowRegisterMap(Register reg)464 void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
465 DCHECK(RegisterIsTemporary(reg));
466 size_t index = GetRegisterInfoTableIndex(reg);
467 if (index >= register_info_table_.size()) {
468 size_t new_size = index + 1;
469 size_t old_size = register_info_table_.size();
470 register_info_table_.resize(new_size);
471 for (size_t i = old_size; i < new_size; ++i) {
472 register_info_table_[i] =
473 zone()->New<RegisterInfo>(RegisterFromRegisterInfoTableIndex(i),
474 NextEquivalenceId(), true, false);
475 }
476 }
477 }
478
AllocateRegister(RegisterInfo * info)479 void BytecodeRegisterOptimizer::AllocateRegister(RegisterInfo* info) {
480 info->set_allocated(true);
481 if (!info->materialized()) {
482 info->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
483 }
484 }
485
RegisterAllocateEvent(Register reg)486 void BytecodeRegisterOptimizer::RegisterAllocateEvent(Register reg) {
487 AllocateRegister(GetOrCreateRegisterInfo(reg));
488 }
489
RegisterListAllocateEvent(RegisterList reg_list)490 void BytecodeRegisterOptimizer::RegisterListAllocateEvent(
491 RegisterList reg_list) {
492 if (reg_list.register_count() != 0) {
493 int first_index = reg_list.first_register().index();
494 GrowRegisterMap(Register(first_index + reg_list.register_count() - 1));
495 for (int i = 0; i < reg_list.register_count(); i++) {
496 AllocateRegister(GetRegisterInfo(Register(first_index + i)));
497 }
498 }
499 }
500
RegisterListFreeEvent(RegisterList reg_list)501 void BytecodeRegisterOptimizer::RegisterListFreeEvent(RegisterList reg_list) {
502 int first_index = reg_list.first_register().index();
503 for (int i = 0; i < reg_list.register_count(); i++) {
504 GetRegisterInfo(Register(first_index + i))->set_allocated(false);
505 }
506 }
507
508 } // namespace interpreter
509 } // namespace internal
510 } // namespace v8
511