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 V8_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