// 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. #include "src/init/v8.h" #include "src/interpreter/bytecode-label.h" #include "src/interpreter/bytecode-register-optimizer.h" #include "test/unittests/interpreter/bytecode-utils.h" #include "test/unittests/test-utils.h" namespace v8 { namespace internal { namespace interpreter { class BytecodeRegisterOptimizerTest : public BytecodeRegisterOptimizer::BytecodeWriter, public TestWithIsolateAndZone { public: struct RegisterTransfer { Bytecode bytecode; Register input; Register output; }; BytecodeRegisterOptimizerTest() = default; ~BytecodeRegisterOptimizerTest() override { delete register_allocator_; } void Initialize(int number_of_parameters, int number_of_locals) { register_allocator_ = new BytecodeRegisterAllocator(number_of_locals); register_optimizer_ = zone()->New( zone(), register_allocator_, number_of_locals, number_of_parameters, this); } void EmitLdar(Register input) override { output_.push_back({Bytecode::kLdar, input, Register()}); } void EmitStar(Register output) override { output_.push_back({Bytecode::kStar, Register(), output}); } void EmitMov(Register input, Register output) override { output_.push_back({Bytecode::kMov, input, output}); } BytecodeRegisterAllocator* allocator() { return register_allocator_; } BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; } Register NewTemporary() { return allocator()->NewRegister(); } void ReleaseTemporaries(Register reg) { allocator()->ReleaseRegisters(reg.index()); } size_t write_count() const { return output_.size(); } const RegisterTransfer& last_written() const { return output_.back(); } const std::vector* output() { return &output_; } private: BytecodeRegisterAllocator* register_allocator_; BytecodeRegisterOptimizer* register_optimizer_; std::vector output_; }; // Sanity tests. TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForFlush) { Initialize(1, 1); Register temp = NewTemporary(); optimizer()->DoStar(temp); CHECK_EQ(write_count(), 0u); optimizer()->Flush(); CHECK_EQ(write_count(), 1u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar); CHECK_EQ(output()->at(0).output.index(), temp.index()); } TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) { Initialize(1, 1); Register temp = NewTemporary(); optimizer()->DoStar(temp); CHECK_EQ(write_count(), 0u); optimizer() ->PrepareForBytecode(); CHECK_EQ(write_count(), 1u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar); CHECK_EQ(output()->at(0).output.index(), temp.index()); } // Basic Register Optimizations TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) { Initialize(3, 1); Register parameter = Register::FromParameterIndex(1); optimizer()->DoLdar(parameter); CHECK_EQ(write_count(), 0u); Register temp = NewTemporary(); optimizer()->DoStar(temp); ReleaseTemporaries(temp); CHECK_EQ(write_count(), 0u); optimizer() ->PrepareForBytecode(); CHECK_EQ(output()->at(0).bytecode, Bytecode::kLdar); CHECK_EQ(output()->at(0).input.index(), parameter.index()); } TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterUsed) { Initialize(3, 1); optimizer() ->PrepareForBytecode(); Register temp0 = NewTemporary(); Register temp1 = NewTemporary(); optimizer()->DoStar(temp1); CHECK_EQ(write_count(), 0u); optimizer() ->PrepareForBytecode(); CHECK_EQ(write_count(), 1u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar); CHECK_EQ(output()->at(0).output.index(), temp1.index()); optimizer()->DoMov(temp1, temp0); CHECK_EQ(write_count(), 1u); ReleaseTemporaries(temp1); CHECK_EQ(write_count(), 1u); optimizer()->DoLdar(temp0); CHECK_EQ(write_count(), 1u); optimizer() ->PrepareForBytecode(); CHECK_EQ(write_count(), 2u); CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar); CHECK_EQ(output()->at(1).input.index(), temp1.index()); } TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterNotFlushed) { Initialize(3, 1); optimizer() ->PrepareForBytecode(); Register temp0 = NewTemporary(); Register temp1 = NewTemporary(); optimizer()->DoStar(temp0); CHECK_EQ(write_count(), 0u); optimizer()->DoStar(temp1); CHECK_EQ(write_count(), 0u); ReleaseTemporaries(temp1); optimizer()->Flush(); CHECK_EQ(write_count(), 1u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar); CHECK_EQ(output()->at(0).output.index(), temp0.index()); } TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) { Initialize(3, 1); Register parameter = Register::FromParameterIndex(1); optimizer()->DoLdar(parameter); CHECK_EQ(write_count(), 0u); Register local = Register(0); optimizer()->DoStar(local); CHECK_EQ(write_count(), 1u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kMov); CHECK_EQ(output()->at(0).input.index(), parameter.index()); CHECK_EQ(output()->at(0).output.index(), local.index()); optimizer() ->PrepareForBytecode(); CHECK_EQ(write_count(), 2u); CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar); CHECK_EQ(output()->at(1).input.index(), local.index()); } TEST_F(BytecodeRegisterOptimizerTest, SingleTemporaryNotMaterializedForInput) { Initialize(3, 1); Register parameter = Register::FromParameterIndex(1); Register temp0 = NewTemporary(); Register temp1 = NewTemporary(); optimizer()->DoMov(parameter, temp0); optimizer()->DoMov(parameter, temp1); CHECK_EQ(write_count(), 0u); Register reg = optimizer()->GetInputRegister(temp0); RegisterList reg_list = optimizer()->GetInputRegisterList( BytecodeUtils::NewRegisterList(temp0.index(), 1)); CHECK_EQ(write_count(), 0u); CHECK_EQ(parameter.index(), reg.index()); CHECK_EQ(parameter.index(), reg_list.first_register().index()); CHECK_EQ(1, reg_list.register_count()); } TEST_F(BytecodeRegisterOptimizerTest, RangeOfTemporariesMaterializedForInput) { Initialize(3, 1); Register parameter = Register::FromParameterIndex(1); Register temp0 = NewTemporary(); Register temp1 = NewTemporary(); optimizer() ->PrepareForBytecode(); optimizer()->DoStar(temp0); optimizer()->DoMov(parameter, temp1); CHECK_EQ(write_count(), 0u); optimizer() ->PrepareForBytecode(); RegisterList reg_list = optimizer()->GetInputRegisterList( BytecodeUtils::NewRegisterList(temp0.index(), 2)); CHECK_EQ(temp0.index(), reg_list.first_register().index()); CHECK_EQ(2, reg_list.register_count()); CHECK_EQ(write_count(), 2u); CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar); CHECK_EQ(output()->at(0).output.index(), temp0.index()); CHECK_EQ(output()->at(1).bytecode, Bytecode::kMov); CHECK_EQ(output()->at(1).input.index(), parameter.index()); CHECK_EQ(output()->at(1).output.index(), temp1.index()); } } // namespace interpreter } // namespace internal } // namespace v8