Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: src/interpreter/bytecode-register-optimizer.cc

Issue 1997653002: [interpreter] Bytecode register optimizer. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Additional comment. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 // Class for maintaining state of a register. In particular, it keeps
12 // track of whether one register is valuewise equivalent to another
13 // register and whether a register is materialized in the bytecode
14 // stream with the current value.
15 class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
16 public:
17 RegisterInfo(Register reg, int equivalence_id, bool materialized)
18 : register_(reg),
19 equivalence_id_(equivalence_id),
20 materialized_(materialized),
21 next_(this),
22 prev_(this) {}
23
24 void AddToEquivalenceSetOf(RegisterInfo* info);
25 void RemoveFromEquivalenceSet();
26 bool IsOnlyEquivalent() const;
27 bool IsOnlyMaterializedEquivalent() const;
28 bool IsEquivalent(RegisterInfo* info) const;
29
30 RegisterInfo* GetMaterializedEquivalent();
31 RegisterInfo* GetMaterializedEquivalentOtherThan(Register reg);
32 RegisterInfo* GetMaterializationCandidate();
33
34 Register register_value() const { return register_; }
35 bool materialized() const { return materialized_; }
36 void set_materialized(bool materialized) { materialized_ = materialized; }
37 void set_equivalence_id(int equivalence_id) {
38 equivalence_id_ = equivalence_id;
39 }
40 int equivalence_id() const { return equivalence_id_; }
41 RegisterInfo* next() const { return next_; }
42
43 private:
44 Register register_;
45 int equivalence_id_;
46 bool materialized_;
47
48 // Equivalence set pointers.
49 RegisterInfo* next_;
50 RegisterInfo* prev_;
51
52 DISALLOW_COPY_AND_ASSIGN(RegisterInfo);
53 };
54
55 void BytecodeRegisterOptimizer::RegisterInfo::AddToEquivalenceSetOf(
56 RegisterInfo* info) {
57 DCHECK(!IsEquivalent(info) && IsOnlyEquivalent());
58 this->next_ = info->next_;
59 this->prev_ = info;
60 this->prev_->next_ = this;
61 this->next_->prev_ = this;
62 set_equivalence_id(info->equivalence_id());
63 }
64
65 void BytecodeRegisterOptimizer::RegisterInfo::RemoveFromEquivalenceSet() {
66 this->next_->prev_ = this->prev_;
67 this->prev_->next_ = this->next_;
68 this->next_ = this->prev_ = this;
69 }
70
71 bool BytecodeRegisterOptimizer::RegisterInfo::IsOnlyEquivalent() const {
72 return this->next_ == this;
73 }
74
75 bool BytecodeRegisterOptimizer::RegisterInfo::IsOnlyMaterializedEquivalent()
76 const {
77 DCHECK(materialized());
78
79 const RegisterInfo* visitor = this->next_;
80 while (visitor != this) {
81 if (visitor->materialized()) {
82 return false;
83 }
84 visitor = visitor->next_;
85 }
86 return true;
87 }
88
89 bool BytecodeRegisterOptimizer::RegisterInfo::IsEquivalent(
90 RegisterInfo* info) const {
91 return equivalence_id() == info->equivalence_id();
92 }
93
94 BytecodeRegisterOptimizer::RegisterInfo*
95 BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalent() {
96 RegisterInfo* visitor = this;
97 do {
98 if (visitor->materialized()) {
99 return visitor;
100 }
101 visitor = visitor->next_;
102 } while (visitor != this);
103
104 return nullptr;
105 }
106
107 BytecodeRegisterOptimizer::RegisterInfo*
108 BytecodeRegisterOptimizer::RegisterInfo::GetMaterializationCandidate() {
109 DCHECK(this->materialized());
110 RegisterInfo* visitor = this->next_;
111 Register best_reg;
112 RegisterInfo* best_info = nullptr;
113 while (visitor != this) {
114 if (visitor->materialized()) {
115 return nullptr;
116 }
117 if (best_info == nullptr ||
118 visitor->register_value() < best_info->register_value()) {
119 best_info = visitor;
120 }
121 visitor = visitor->next_;
122 }
123 return best_info;
124 }
125
126 BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
127 Zone* zone, TemporaryRegisterAllocator* register_allocator,
128 int parameter_count, int fixed_register_count,
129 BytecodePipelineStage* next_stage)
130 : accumulator_(Register::bytecode_array()),
131 temporary_base_(register_allocator->allocation_base()),
132 register_map_(zone),
133 equivalence_id_(0),
134 next_stage_(next_stage),
135 flushed_(false),
136 zone_(zone) {
137 register_allocator->set_observer(this);
138
139 // Calculate offset so register index values can be mapped into
140 // a vector of register metadata.
141 if (parameter_count != 0) {
142 map_index_offset_ =
143 -Register::FromParameterIndex(0, parameter_count).index();
144 } else {
145 map_index_offset_ = -accumulator_.index();
146 }
147
148 // Initialize register map for parameters, locals, and the
149 // accumulator.
150 register_map_.resize(map_index_offset_ +
151 static_cast<size_t>(fixed_register_count));
152 for (size_t i = 0; i < register_map_.size(); ++i) {
153 register_map_[i] = new (zone)
154 RegisterInfo(RegisterFromMapIndex(i), equivalence_id_++, true);
155 DCHECK_EQ(register_map_[i]->register_value().index(),
156 RegisterFromMapIndex(i).index());
157 }
158 accumulator_info_ = GetRegisterInfo(accumulator_);
159 DCHECK(accumulator_info_->register_value() == accumulator_);
160 }
161
162 void BytecodeRegisterOptimizer::FlushState() {
163 if (flushed_) {
164 return;
165 }
166
167 // Materialize all live registers.
168 size_t count = register_map_.size();
169 for (size_t i = 0; i < count; ++i) {
170 RegisterInfo* reg_info = register_map_[i];
171 if (!reg_info->materialized() && !reg_info->IsOnlyEquivalent()) {
172 DCHECK(RegisterIsTemporary(reg_info->register_value()) ||
173 reg_info->register_value() == accumulator_);
174 Materialize(reg_info);
175 }
176 }
177
178 // Break all existing equivalences.
179 for (size_t i = 0; i < count; ++i) {
180 RegisterInfo* reg_info = register_map_[i];
181 if (!reg_info->IsOnlyEquivalent()) {
182 reg_info->RemoveFromEquivalenceSet();
183 reg_info->set_equivalence_id(equivalence_id_++);
184 }
185 }
186 flushed_ = true;
187 }
188
189 // override
190 void BytecodeRegisterOptimizer::FlushBasicBlock() {
191 FlushState();
192 next_stage_->FlushBasicBlock();
193 }
194
195 // override
196 size_t BytecodeRegisterOptimizer::FlushForOffset() {
197 FlushState();
198 return next_stage_->FlushForOffset();
199 }
200
201 void BytecodeRegisterOptimizer::EmitNopTakingSourcePosition(
202 BytecodeNode* node) {
203 BytecodeNode nop(Bytecode::kNop);
204 nop.source_info().Update(node->source_info());
205 WriteToNextStage(&nop);
206 node->source_info().set_invalid();
207 }
208
209 // override
210 void BytecodeRegisterOptimizer::Write(BytecodeNode* node) {
211 flushed_ = false;
212
213 //
214 // The logic for register to register transfer here emits Nops with the
215 // current source position.
216 //
217 // Transfers with observable registers as the destination will be
218 // immediately materialized so the source position information will
219 // be ordered correctly.
220 //
221 // Transfers without observable destination registers will initially
222 // be emitted as Nop's with the source position. They may, or may
223 // not, be materialized by the optimizer. However, the source
224 // position is not lost and being attachd to a Nop is fine as the
225 // destination register is not observable in the debugger.
226 //
227 switch (node->bytecode()) {
228 case Bytecode::kLdar: {
229 EmitNopTakingSourcePosition(node);
230 DoLdar(node);
231 return;
232 }
233 case Bytecode::kStar: {
234 EmitNopTakingSourcePosition(node);
235 DoStar(node);
236 return;
237 }
238 case Bytecode::kMov: {
239 EmitNopTakingSourcePosition(node);
240 DoMov(node);
241 return;
242 }
243 default:
244 break;
245 }
246
247 if (Bytecodes::IsJump(node->bytecode()) ||
248 node->bytecode() == Bytecode::kDebugger) {
249 // The debugger can manipulate locals and parameters, flush
250 // everything before handing over to it. Similarly, all state must
251 // be flushed before emitting a jump due to how bytecode offsets
252 // for jumps are evaluated.
253 FlushState();
254 WriteToNextStage(node);
255 return;
256 }
257
258 PrepareOperands(node);
259 WriteToNextStage(node);
260 }
261
262 void BytecodeRegisterOptimizer::WriteToNextStage(BytecodeNode* node) {
263 next_stage_->Write(node);
264 }
265
266 void BytecodeRegisterOptimizer::OutputRegisterTransfer(
267 RegisterInfo* input_info, RegisterInfo* output_info) {
268 Register input = input_info->register_value();
269 Register output = output_info->register_value();
270 DCHECK_NE(input.index(), output.index());
271
272 if (input == accumulator_) {
273 uint32_t operand = static_cast<uint32_t>(output.ToOperand());
274 OperandScale scale = Bytecodes::OperandSizesToScale(output.SizeOfOperand());
275 BytecodeNode node(Bytecode::kStar, operand, scale);
276 WriteToNextStage(&node);
277 } else if (output == accumulator_) {
278 uint32_t operand = static_cast<uint32_t>(input.ToOperand());
279 OperandScale scale = Bytecodes::OperandSizesToScale(input.SizeOfOperand());
280 BytecodeNode node(Bytecode::kLdar, operand, scale);
281 WriteToNextStage(&node);
282 } else {
283 uint32_t operand0 = static_cast<uint32_t>(input.ToOperand());
284 uint32_t operand1 = static_cast<uint32_t>(output.ToOperand());
285 OperandScale scale = Bytecodes::OperandSizesToScale(input.SizeOfOperand(),
286 output.SizeOfOperand());
287 BytecodeNode node(Bytecode::kMov, operand0, operand1, scale);
288 WriteToNextStage(&node);
289 }
290 }
291
292 void BytecodeRegisterOptimizer::CreateMaterializedEquivalentIfRequired(
293 RegisterInfo* info) {
294 if (info->materialized()) {
295 RegisterInfo* unmaterialized = info->GetMaterializationCandidate();
296 if (unmaterialized) {
297 OutputRegisterTransfer(info, unmaterialized);
298 unmaterialized->set_materialized(true);
299 }
300 }
301 }
302
303 BytecodeRegisterOptimizer::RegisterInfo*
304 BytecodeRegisterOptimizer::GetMaterializedEquivalent(RegisterInfo* info) {
305 return info->materialized() ? info : info->GetMaterializedEquivalent();
306 }
307
308 BytecodeRegisterOptimizer::RegisterInfo*
309 BytecodeRegisterOptimizer::GetMaterializedEquivalentNotAccumulator(
310 RegisterInfo* info) {
311 if (info->materialized()) {
312 return info;
313 }
314
315 RegisterInfo* result = info->GetMaterializedEquivalent();
316 if (result->register_value() == accumulator_) {
317 result = result->next()->GetMaterializedEquivalent();
318 if (result->register_value() == accumulator_) {
319 Materialize(info);
320 result = info;
321 }
322 }
323 return result;
324 }
325
326 void BytecodeRegisterOptimizer::Materialize(RegisterInfo* info) {
327 if (!info->materialized()) {
328 RegisterInfo* materialized = info->GetMaterializedEquivalent();
329 OutputRegisterTransfer(materialized, info);
330 info->set_materialized(true);
331 }
332 }
333
334 void BytecodeRegisterOptimizer::RegisterTransfer(RegisterInfo* input_info,
335 RegisterInfo* output_info) {
336 // Ensure value associated with output is materialized into a
337 // different register if necessary.
338 CreateMaterializedEquivalentIfRequired(output_info);
339 bool output_is_observable =
340 RegisterIsObservable(output_info->register_value());
341
342 // If the output and input are not equivalent or the output is
343 // visible to the user/debugger, update equivalence set and
344 // potentially emit store if the transfer is observable.
345 if (!output_info->IsEquivalent(input_info) || output_is_observable) {
346 // Remove from current set and invalidate equivalence id.
347 output_info->RemoveFromEquivalenceSet();
348 output_info->set_equivalence_id(equivalence_id_++);
349
350 // Add to the equivalence set of |input_info|.
351 RegisterInfo* materialized_info = input_info->GetMaterializedEquivalent();
352 output_info->AddToEquivalenceSetOf(materialized_info);
353 output_info->set_materialized(output_is_observable);
354 if (output_is_observable) {
355 OutputRegisterTransfer(materialized_info, output_info);
356 }
357 }
358 }
359
360 void BytecodeRegisterOptimizer::DoLdar(const BytecodeNode* const node) {
361 Register input = GetRegisterInputOperand(
362 0, node->bytecode(), node->operands(), node->operand_count());
363 RegisterInfo* input_info = GetRegisterInfo(input);
364 RegisterTransfer(input_info, accumulator_info_);
365 }
366
367 void BytecodeRegisterOptimizer::DoMov(const BytecodeNode* const node) {
368 Register input = GetRegisterInputOperand(
369 0, node->bytecode(), node->operands(), node->operand_count());
370 RegisterInfo* input_info = GetRegisterInfo(input);
371 Register output = GetRegisterOutputOperand(
372 1, node->bytecode(), node->operands(), node->operand_count());
373 RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
374 RegisterTransfer(input_info, output_info);
375 }
376
377 void BytecodeRegisterOptimizer::DoStar(const BytecodeNode* const node) {
378 Register output = GetRegisterOutputOperand(
379 0, node->bytecode(), node->operands(), node->operand_count());
380 RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
381 RegisterTransfer(accumulator_info_, output_info);
382 }
383
384 void BytecodeRegisterOptimizer::PrepareRegisterOutputOperand(
385 RegisterInfo* reg_info) {
386 CreateMaterializedEquivalentIfRequired(reg_info);
387
388 // Put register in it's own equivalence set.
389 reg_info->RemoveFromEquivalenceSet();
390 reg_info->set_materialized(true);
391 reg_info->set_equivalence_id(equivalence_id_++);
392 }
393
394 void BytecodeRegisterOptimizer::PrepareRegisterRangeOutputOperand(
395 Register start, int count) {
396 for (int i = 0; i < count; ++i) {
397 Register reg(start.index() + i);
398 RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
399 PrepareRegisterOutputOperand(reg_info);
400 }
401 }
402
403 Register BytecodeRegisterOptimizer::PrepareRegisterInputOperand(Register reg) {
404 RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
405 if (reg_info->materialized()) {
406 return reg;
407 } else {
408 RegisterInfo* equivalent_info =
409 GetMaterializedEquivalentNotAccumulator(reg_info);
410 return equivalent_info->register_value();
411 }
412 }
413
414 void BytecodeRegisterOptimizer::PrepareRegisterRangeInputOperand(Register start,
415 int count) {
416 for (int i = 0; i < count; ++i) {
417 Register current(start.index() + i);
418 RegisterInfo* input_info = GetRegisterInfo(current);
419 Materialize(input_info);
420 }
421 }
422
423 void BytecodeRegisterOptimizer::PrepareRegisterOperands(
424 BytecodeNode* const node) {
425 //
426 // For each input operand, get a materialized equivalent if it is
427 // just a single register, otherwise materialize register range.
428 // Update operand_scale if necessary.
429 //
430 // For each output register about to be clobbered, materialize an
431 // equivalent if it exists. Put each register in it's own equivalence set.
432 //
433 int register_operand_bitmap =
434 Bytecodes::GetRegisterOperandBitmap(node->bytecode());
435 const OperandType* operand_types =
436 Bytecodes::GetOperandTypes(node->bytecode());
437 uint32_t* operands = node->operands();
438 for (int i = 0; register_operand_bitmap != 0;
439 ++i, register_operand_bitmap >>= 1) {
440 if ((register_operand_bitmap & 1) == 0) {
441 continue;
442 }
443 OperandType operand_type = operand_types[i];
444 int count = 0;
445 if (operand_types[i + 1] == OperandType::kRegCount) {
446 count = static_cast<int>(operands[i + 1]);
447 if (count == 0) {
448 continue;
449 }
450 } else {
451 count = Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
452 }
453
454 Register reg = Register::FromOperand(static_cast<int32_t>(operands[i]));
455 if (Bytecodes::IsRegisterInputOperandType(operand_type)) {
456 if (count == 1) {
457 Register equivalent = PrepareRegisterInputOperand(reg);
458 operands[i] = static_cast<uint32_t>(equivalent.ToOperand());
459 // Update operand scale as equivalent may be different.
460 OperandScale operand_scale =
461 Bytecodes::OperandSizesToScale(equivalent.SizeOfOperand());
462 if (operand_scale > node->operand_scale()) {
463 node->set_operand_scale(operand_scale);
464 }
465 } else if (count > 1) {
466 PrepareRegisterRangeInputOperand(reg, count);
467 }
468 } else if (Bytecodes::IsRegisterOutputOperandType(operand_type)) {
469 PrepareRegisterRangeOutputOperand(reg, count);
470 }
471 }
472 }
473
474 void BytecodeRegisterOptimizer::PrepareAccumulator(BytecodeNode* const node) {
475 // Materialize the accumulator if it is read by the bytecode. The
476 // accumulator is special and no other register can be materialized
477 // in it's place.
478 if (Bytecodes::ReadsAccumulator(node->bytecode()) &&
479 !accumulator_info_->materialized()) {
480 Materialize(accumulator_info_);
481 }
482
483 // Materialize an equivalent to the accumulator if it will be
484 // clobbered when the bytecode is dispatched.
485 if (Bytecodes::WritesAccumulator(node->bytecode())) {
486 PrepareRegisterOutputOperand(accumulator_info_);
487 }
488 }
489
490 void BytecodeRegisterOptimizer::PrepareOperands(BytecodeNode* const node) {
491 PrepareAccumulator(node);
492 PrepareRegisterOperands(node);
493 }
494
495 // static
496 Register BytecodeRegisterOptimizer::GetRegisterInputOperand(
497 int index, Bytecode bytecode, const uint32_t* operands, int operand_count) {
498 DCHECK_LT(index, operand_count);
499 DCHECK(Bytecodes::IsRegisterInputOperandType(
500 Bytecodes::GetOperandType(bytecode, index)));
501 return OperandToRegister(operands[index]);
502 }
503
504 // static
505 Register BytecodeRegisterOptimizer::GetRegisterOutputOperand(
506 int index, Bytecode bytecode, const uint32_t* operands, int operand_count) {
507 DCHECK_LT(index, operand_count);
508 DCHECK(Bytecodes::IsRegisterOutputOperandType(
509 Bytecodes::GetOperandType(bytecode, index)));
510 return OperandToRegister(operands[index]);
511 }
512
513 BytecodeRegisterOptimizer::RegisterInfo*
514 BytecodeRegisterOptimizer::GetRegisterInfo(Register reg) {
515 size_t index = GetMapIndex(reg);
516 return (index < register_map_.size()) ? register_map_[index] : nullptr;
517 }
518
519 BytecodeRegisterOptimizer::RegisterInfo*
520 BytecodeRegisterOptimizer::GetOrCreateRegisterInfo(Register reg) {
521 size_t index = GetMapIndex(reg);
522 return index < register_map_.size() ? register_map_[index]
523 : NewRegisterInfo(reg);
524 }
525
526 BytecodeRegisterOptimizer::RegisterInfo*
527 BytecodeRegisterOptimizer::NewRegisterInfo(Register reg) {
528 size_t index = GetMapIndex(reg);
529 DCHECK_GE(index, register_map_.size());
530 GrowRegisterMap(reg);
531 return register_map_[index];
532 }
533
534 void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
535 DCHECK(RegisterIsTemporary(reg));
536 size_t index = GetMapIndex(reg);
537 DCHECK_GE(index, register_map_.size());
538 size_t new_size = index + 1;
539 size_t old_size = register_map_.size();
540 register_map_.resize(new_size);
541 for (size_t i = old_size; i < new_size; ++i) {
542 register_map_[i] = new (zone())
543 RegisterInfo(RegisterFromMapIndex(i), equivalence_id_++, false);
544 }
545 }
546
547 void BytecodeRegisterOptimizer::TemporaryRegisterFreeEvent(Register reg) {
548 RegisterInfo* info = GetRegisterInfo(reg);
549 if (info != nullptr) {
550 // If register is materialized and part of equivalence set, make
551 // sure another member of the set holds the value before the
552 // temporary register is removed.
553 CreateMaterializedEquivalentIfRequired(info);
554 info->RemoveFromEquivalenceSet();
555 info->set_materialized(false);
556 info->set_equivalence_id(-1);
557 }
558 }
559
560 } // namespace interpreter
561 } // namespace internal
562 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698