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 #ifndef V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
6 #define V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
7 
8 #include "src/base/compiler-specific.h"
9 #include "src/globals.h"
10 #include "src/interpreter/bytecode-register-allocator.h"
11 
12 namespace v8 {
13 namespace internal {
14 namespace interpreter {
15 
16 // An optimization stage for eliminating unnecessary transfers between
17 // registers. The bytecode generator uses temporary registers
18 // liberally for correctness and convenience and this stage removes
19 // transfers that are not required and preserves correctness.
20 class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
NON_EXPORTED_BASE(BytecodeRegisterAllocator::Observer)21     : public NON_EXPORTED_BASE(BytecodeRegisterAllocator::Observer),
22       public NON_EXPORTED_BASE(ZoneObject) {
23  public:
24   class BytecodeWriter {
25    public:
26     BytecodeWriter() {}
27     virtual ~BytecodeWriter() {}
28 
29     // Called to emit a register transfer bytecode.
30     virtual void EmitLdar(Register input) = 0;
31     virtual void EmitStar(Register output) = 0;
32     virtual void EmitMov(Register input, Register output) = 0;
33 
34    private:
35     DISALLOW_COPY_AND_ASSIGN(BytecodeWriter);
36   };
37 
38   BytecodeRegisterOptimizer(Zone* zone,
39                             BytecodeRegisterAllocator* register_allocator,
40                             int fixed_registers_count, int parameter_count,
41                             BytecodeWriter* bytecode_writer);
42   virtual ~BytecodeRegisterOptimizer() {}
43 
44   // Perform explicit register transfer operations.
45   void DoLdar(Register input) {
46     // TODO(rmcilroy): Avoid treating accumulator loads as clobbering the
47     // accumulator until the value is actually materialized in the accumulator.
48     RegisterInfo* input_info = GetRegisterInfo(input);
49     RegisterTransfer(input_info, accumulator_info_);
50   }
51   void DoStar(Register output) {
52     RegisterInfo* output_info = GetRegisterInfo(output);
53     RegisterTransfer(accumulator_info_, output_info);
54   }
55   void DoMov(Register input, Register output) {
56     RegisterInfo* input_info = GetRegisterInfo(input);
57     RegisterInfo* output_info = GetRegisterInfo(output);
58     RegisterTransfer(input_info, output_info);
59   }
60 
61   // Materialize all live registers and flush equivalence sets.
62   void Flush();
63 
64   // Prepares for |bytecode|.
65   template <Bytecode bytecode, AccumulatorUse accumulator_use>
66   INLINE(void PrepareForBytecode()) {
67     if (Bytecodes::IsJump(bytecode) || Bytecodes::IsSwitch(bytecode) ||
68         bytecode == Bytecode::kDebugger ||
69         bytecode == Bytecode::kSuspendGenerator ||
70         bytecode == Bytecode::kResumeGenerator) {
71       // All state must be flushed before emitting
72       // - a jump bytecode (as the register equivalents at the jump target
73       //   aren't known)
74       // - a switch bytecode (as the register equivalents at the switch targets
75       //   aren't known)
76       // - a call to the debugger (as it can manipulate locals and parameters),
77       // - a generator suspend (as this involves saving all registers).
78       // - a generator register restore.
79       Flush();
80     }
81 
82     // Materialize the accumulator if it is read by the bytecode. The
83     // accumulator is special and no other register can be materialized
84     // in it's place.
85     if (BytecodeOperands::ReadsAccumulator(accumulator_use)) {
86       Materialize(accumulator_info_);
87     }
88 
89     // Materialize an equivalent to the accumulator if it will be
90     // clobbered when the bytecode is dispatched.
91     if (BytecodeOperands::WritesAccumulator(accumulator_use)) {
92       PrepareOutputRegister(accumulator_);
93     }
94   }
95 
96   // Prepares |reg| for being used as an output operand.
97   void PrepareOutputRegister(Register reg);
98 
99   // Prepares registers in |reg_list| for being used as an output operand.
100   void PrepareOutputRegisterList(RegisterList reg_list);
101 
102   // Returns an equivalent register to |reg| to be used as an input operand.
103   Register GetInputRegister(Register reg);
104 
105   // Returns an equivalent register list to |reg_list| to be used as an input
106   // operand.
107   RegisterList GetInputRegisterList(RegisterList reg_list);
108 
109   int maxiumum_register_index() const { return max_register_index_; }
110 
111  private:
112   static const uint32_t kInvalidEquivalenceId;
113 
114   class RegisterInfo;
115 
116   // BytecodeRegisterAllocator::Observer interface.
117   void RegisterAllocateEvent(Register reg) override;
118   void RegisterListAllocateEvent(RegisterList reg_list) override;
119   void RegisterListFreeEvent(RegisterList reg) override;
120 
121   // Update internal state for register transfer from |input| to |output|
122   void RegisterTransfer(RegisterInfo* input, RegisterInfo* output);
123 
124   // Emit a register transfer bytecode from |input| to |output|.
125   void OutputRegisterTransfer(RegisterInfo* input, RegisterInfo* output);
126 
127   void CreateMaterializedEquivalent(RegisterInfo* info);
128   RegisterInfo* GetMaterializedEquivalent(RegisterInfo* info);
129   RegisterInfo* GetMaterializedEquivalentNotAccumulator(RegisterInfo* info);
130   void Materialize(RegisterInfo* info);
131   void AddToEquivalenceSet(RegisterInfo* set_member,
132                            RegisterInfo* non_set_member);
133 
134   void PushToRegistersNeedingFlush(RegisterInfo* reg);
135   bool EnsureAllRegistersAreFlushed() const;
136 
137   // Methods for finding and creating metadata for each register.
138   RegisterInfo* GetRegisterInfo(Register reg) {
139     size_t index = GetRegisterInfoTableIndex(reg);
140     DCHECK_LT(index, register_info_table_.size());
141     return register_info_table_[index];
142   }
143   RegisterInfo* GetOrCreateRegisterInfo(Register reg) {
144     size_t index = GetRegisterInfoTableIndex(reg);
145     return index < register_info_table_.size() ? register_info_table_[index]
146                                                : NewRegisterInfo(reg);
147   }
148   RegisterInfo* NewRegisterInfo(Register reg) {
149     size_t index = GetRegisterInfoTableIndex(reg);
150     DCHECK_GE(index, register_info_table_.size());
151     GrowRegisterMap(reg);
152     return register_info_table_[index];
153   }
154 
155   void GrowRegisterMap(Register reg);
156 
157   bool RegisterIsTemporary(Register reg) const {
158     return reg >= temporary_base_;
159   }
160 
161   bool RegisterIsObservable(Register reg) const {
162     return reg != accumulator_ && !RegisterIsTemporary(reg);
163   }
164 
165   static Register OperandToRegister(uint32_t operand) {
166     return Register::FromOperand(static_cast<int32_t>(operand));
167   }
168 
169   size_t GetRegisterInfoTableIndex(Register reg) const {
170     return static_cast<size_t>(reg.index() + register_info_table_offset_);
171   }
172 
173   Register RegisterFromRegisterInfoTableIndex(size_t index) const {
174     return Register(static_cast<int>(index) - register_info_table_offset_);
175   }
176 
177   uint32_t NextEquivalenceId() {
178     equivalence_id_++;
179     // TODO(rmcilroy): use the same type for these and remove static_cast.
180     CHECK_NE(static_cast<size_t>(equivalence_id_), kInvalidEquivalenceId);
181     return equivalence_id_;
182   }
183 
184   void AllocateRegister(RegisterInfo* info);
185 
186   Zone* zone() { return zone_; }
187 
188   const Register accumulator_;
189   RegisterInfo* accumulator_info_;
190   const Register temporary_base_;
191   int max_register_index_;
192 
193   // Direct mapping to register info.
194   ZoneVector<RegisterInfo*> register_info_table_;
195   int register_info_table_offset_;
196 
197   ZoneDeque<RegisterInfo*> registers_needing_flushed_;
198 
199   // Counter for equivalence sets identifiers.
200   int equivalence_id_;
201 
202   BytecodeWriter* bytecode_writer_;
203   bool flush_required_;
204   Zone* zone_;
205 
206   DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterOptimizer);
207 };
208 
209 }  // namespace interpreter
210 }  // namespace internal
211 }  // namespace v8
212 
213 #endif  // V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
214