aboutsummaryrefslogtreecommitdiff
path: root/src/interpreter/bytecode-register-optimizer.h
blob: 80c2f2587ffa8da6b2175e706729e506bf7d12ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_

#include "src/base/compiler-specific.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-pipeline.h"

namespace v8 {
namespace internal {
namespace interpreter {

// An optimization stage for eliminating unnecessary transfers between
// registers. The bytecode generator uses temporary registers
// liberally for correctness and convenience and this stage removes
// transfers that are not required and preserves correctness.
class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
    : public NON_EXPORTED_BASE(BytecodeRegisterAllocator::Observer),
      public NON_EXPORTED_BASE(ZoneObject) {
 public:
  BytecodeRegisterOptimizer(Zone* zone,
                            BytecodeRegisterAllocator* register_allocator,
                            int fixed_registers_count, int parameter_count,
                            BytecodePipelineStage* next_stage);
  virtual ~BytecodeRegisterOptimizer() {}

  // Perform explicit register transfer operations.
  void DoLdar(Register input, BytecodeSourceInfo source_info) {
    RegisterInfo* input_info = GetRegisterInfo(input);
    RegisterTransfer(input_info, accumulator_info_, source_info);
  }
  void DoStar(Register output, BytecodeSourceInfo source_info) {
    RegisterInfo* output_info = GetRegisterInfo(output);
    RegisterTransfer(accumulator_info_, output_info, source_info);
  }
  void DoMov(Register input, Register output, BytecodeSourceInfo source_info) {
    RegisterInfo* input_info = GetRegisterInfo(input);
    RegisterInfo* output_info = GetRegisterInfo(output);
    RegisterTransfer(input_info, output_info, source_info);
  }

  // Materialize all live registers and flush equivalence sets.
  void Flush();

  // Prepares for |bytecode|.
  template <Bytecode bytecode, AccumulatorUse accumulator_use>
  INLINE(void PrepareForBytecode()) {
    if (Bytecodes::IsJump(bytecode) || bytecode == Bytecode::kDebugger ||
        bytecode == Bytecode::kSuspendGenerator) {
      // All state must be flushed before emitting
      // - a jump bytecode (as the register equivalents at the jump target
      // aren't
      //   known.
      // - a call to the debugger (as it can manipulate locals and parameters),
      // - a generator suspend (as this involves saving all registers).
      Flush();
    }

    // Materialize the accumulator if it is read by the bytecode. The
    // accumulator is special and no other register can be materialized
    // in it's place.
    if (BytecodeOperands::ReadsAccumulator(accumulator_use)) {
      Materialize(accumulator_info_);
    }

    // Materialize an equivalent to the accumulator if it will be
    // clobbered when the bytecode is dispatched.
    if (BytecodeOperands::WritesAccumulator(accumulator_use)) {
      PrepareOutputRegister(accumulator_);
    }
  }

  // Prepares |reg| for being used as an output operand.
  void PrepareOutputRegister(Register reg);

  // Prepares registers in |reg_list| for being used as an output operand.
  void PrepareOutputRegisterList(RegisterList reg_list);

  // Returns an equivalent register to |reg| to be used as an input operand.
  Register GetInputRegister(Register reg);

  // Returns an equivalent register list to |reg_list| to be used as an input
  // operand.
  RegisterList GetInputRegisterList(RegisterList reg_list);

  int maxiumum_register_index() const { return max_register_index_; }

 private:
  static const uint32_t kInvalidEquivalenceId;

  class RegisterInfo;

  // BytecodeRegisterAllocator::Observer interface.
  void RegisterAllocateEvent(Register reg) override;
  void RegisterListAllocateEvent(RegisterList reg_list) override;
  void RegisterListFreeEvent(RegisterList reg) override;

  // Update internal state for register transfer from |input| to
  // |output| using |source_info| as source position information if
  // any bytecodes are emitted due to transfer.
  void RegisterTransfer(RegisterInfo* input, RegisterInfo* output,
                        BytecodeSourceInfo source_info);

  // Emit a register transfer bytecode from |input| to |output|.
  void OutputRegisterTransfer(
      RegisterInfo* input, RegisterInfo* output,
      BytecodeSourceInfo source_info = BytecodeSourceInfo());

  // Emits a Nop to preserve source position information in the
  // bytecode pipeline.
  void EmitNopForSourceInfo(BytecodeSourceInfo source_info) const;

  void CreateMaterializedEquivalent(RegisterInfo* info);
  RegisterInfo* GetMaterializedEquivalent(RegisterInfo* info);
  RegisterInfo* GetMaterializedEquivalentNotAccumulator(RegisterInfo* info);
  void Materialize(RegisterInfo* info);
  void AddToEquivalenceSet(RegisterInfo* set_member,
                           RegisterInfo* non_set_member);

  // Methods for finding and creating metadata for each register.
  RegisterInfo* GetRegisterInfo(Register reg) {
    size_t index = GetRegisterInfoTableIndex(reg);
    DCHECK_LT(index, register_info_table_.size());
    return register_info_table_[index];
  }
  RegisterInfo* GetOrCreateRegisterInfo(Register reg) {
    size_t index = GetRegisterInfoTableIndex(reg);
    return index < register_info_table_.size() ? register_info_table_[index]
                                               : NewRegisterInfo(reg);
  }
  RegisterInfo* NewRegisterInfo(Register reg) {
    size_t index = GetRegisterInfoTableIndex(reg);
    DCHECK_GE(index, register_info_table_.size());
    GrowRegisterMap(reg);
    return register_info_table_[index];
  }

  void GrowRegisterMap(Register reg);

  bool RegisterIsTemporary(Register reg) const {
    return reg >= temporary_base_;
  }

  bool RegisterIsObservable(Register reg) const {
    return reg != accumulator_ && !RegisterIsTemporary(reg);
  }

  static Register OperandToRegister(uint32_t operand) {
    return Register::FromOperand(static_cast<int32_t>(operand));
  }

  size_t GetRegisterInfoTableIndex(Register reg) const {
    return static_cast<size_t>(reg.index() + register_info_table_offset_);
  }

  Register RegisterFromRegisterInfoTableIndex(size_t index) const {
    return Register(static_cast<int>(index) - register_info_table_offset_);
  }

  uint32_t NextEquivalenceId() {
    equivalence_id_++;
    // TODO(rmcilroy): use the same type for these and remove static_cast.
    CHECK_NE(static_cast<size_t>(equivalence_id_), kInvalidEquivalenceId);
    return equivalence_id_;
  }

  Zone* zone() { return zone_; }

  const Register accumulator_;
  RegisterInfo* accumulator_info_;
  const Register temporary_base_;
  int max_register_index_;

  // Direct mapping to register info.
  ZoneVector<RegisterInfo*> register_info_table_;
  int register_info_table_offset_;

  // Counter for equivalence sets identifiers.
  int equivalence_id_;

  BytecodePipelineStage* next_stage_;
  bool flush_required_;
  Zone* zone_;

  DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterOptimizer);
};

}  // namespace interpreter
}  // namespace internal
}  // namespace v8

#endif  // V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_