// 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 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(operand)); } size_t GetRegisterInfoTableIndex(Register reg) const { return static_cast(reg.index() + register_info_table_offset_); } Register RegisterFromRegisterInfoTableIndex(size_t index) const { return Register(static_cast(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(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 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_