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