| // 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-register-allocator.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: | |
| class BytecodeWriter { | |
| public: | |
| BytecodeWriter() {} | |
| virtual ~BytecodeWriter() {} | |
| // Called to emit a register transfer bytecode. | |
| virtual void EmitLdar(Register input) = 0; | |
| virtual void EmitStar(Register output) = 0; | |
| virtual void EmitMov(Register input, Register output) = 0; | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(BytecodeWriter); | |
| }; | |
| BytecodeRegisterOptimizer(Zone* zone, | |
| BytecodeRegisterAllocator* register_allocator, | |
| int fixed_registers_count, int parameter_count, | |
| BytecodeWriter* bytecode_writer); | |
| virtual ~BytecodeRegisterOptimizer() {} | |
| // Perform explicit register transfer operations. | |
| void DoLdar(Register input) { | |
| // TODO(rmcilroy): Avoid treating accumulator loads as clobbering the | |
| // accumulator until the value is actually materialized in the accumulator. | |
| RegisterInfo* input_info = GetRegisterInfo(input); | |
| RegisterTransfer(input_info, accumulator_info_); | |
| } | |
| void DoStar(Register output) { | |
| RegisterInfo* output_info = GetRegisterInfo(output); | |
| RegisterTransfer(accumulator_info_, output_info); | |
| } | |
| void DoMov(Register input, Register output) { | |
| RegisterInfo* input_info = GetRegisterInfo(input); | |
| RegisterInfo* output_info = GetRegisterInfo(output); | |
| RegisterTransfer(input_info, output_info); | |
| } | |
| // Materialize all live registers and flush equivalence sets. | |
| void Flush(); | |
| // Prepares for |bytecode|. | |
| template <Bytecode bytecode, AccumulatorUse accumulator_use> | |
| V8_INLINE void PrepareForBytecode() { | |
| if (Bytecodes::IsJump(bytecode) || Bytecodes::IsSwitch(bytecode) || | |
| bytecode == Bytecode::kDebugger || | |
| bytecode == Bytecode::kSuspendGenerator || | |
| bytecode == Bytecode::kResumeGenerator) { | |
| // All state must be flushed before emitting | |
| // - a jump bytecode (as the register equivalents at the jump target | |
| // aren't known) | |
| // - a switch bytecode (as the register equivalents at the switch targets | |
| // aren't known) | |
| // - a call to the debugger (as it can manipulate locals and parameters), | |
| // - a generator suspend (as this involves saving all registers). | |
| // - a generator register restore. | |
| 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| | |
| void RegisterTransfer(RegisterInfo* input, RegisterInfo* output); | |
| // Emit a register transfer bytecode from |input| to |output|. | |
| void OutputRegisterTransfer(RegisterInfo* input, RegisterInfo* output); | |
| 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); | |
| void PushToRegistersNeedingFlush(RegisterInfo* reg); | |
| bool EnsureAllRegistersAreFlushed() const; | |
| // 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_; | |
| } | |
| void AllocateRegister(RegisterInfo* info); | |
| 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_; | |
| ZoneDeque<RegisterInfo*> registers_needing_flushed_; | |
| // Counter for equivalence sets identifiers. | |
| int equivalence_id_; | |
| BytecodeWriter* bytecode_writer_; | |
| bool flush_required_; | |
| Zone* zone_; | |
| DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterOptimizer); | |
| }; | |
| } // namespace interpreter | |
| } // namespace internal | |
| } // namespace v8 | |
| #endif // V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_ |