forked from LeenkxTeam/Kmake
224 lines
7.7 KiB
C++
224 lines
7.7 KiB
C++
|
|
// 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<BytecodeRegisterOptimizer>(
|
||
|
|
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<RegisterTransfer>* output() { return &output_; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
BytecodeRegisterAllocator* register_allocator_;
|
||
|
|
BytecodeRegisterOptimizer* register_optimizer_;
|
||
|
|
|
||
|
|
std::vector<RegisterTransfer> 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<Bytecode::kJump, ImplicitRegisterUse::kNone>();
|
||
|
|
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<Bytecode::kReturn,
|
||
|
|
ImplicitRegisterUse::kReadAccumulator>();
|
||
|
|
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<Bytecode::kLdaSmi,
|
||
|
|
ImplicitRegisterUse::kWriteAccumulator>();
|
||
|
|
Register temp0 = NewTemporary();
|
||
|
|
Register temp1 = NewTemporary();
|
||
|
|
optimizer()->DoStar(temp1);
|
||
|
|
CHECK_EQ(write_count(), 0u);
|
||
|
|
optimizer()
|
||
|
|
->PrepareForBytecode<Bytecode::kLdaSmi,
|
||
|
|
ImplicitRegisterUse::kWriteAccumulator>();
|
||
|
|
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<Bytecode::kReturn,
|
||
|
|
ImplicitRegisterUse::kReadAccumulator>();
|
||
|
|
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<Bytecode::kLdaSmi,
|
||
|
|
ImplicitRegisterUse::kWriteAccumulator>();
|
||
|
|
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<Bytecode::kReturn,
|
||
|
|
ImplicitRegisterUse::kReadAccumulator>();
|
||
|
|
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<Bytecode::kLdaSmi,
|
||
|
|
ImplicitRegisterUse::kWriteAccumulator>();
|
||
|
|
optimizer()->DoStar(temp0);
|
||
|
|
optimizer()->DoMov(parameter, temp1);
|
||
|
|
CHECK_EQ(write_count(), 0u);
|
||
|
|
|
||
|
|
optimizer()
|
||
|
|
->PrepareForBytecode<Bytecode::kCallJSRuntime,
|
||
|
|
ImplicitRegisterUse::kWriteAccumulator>();
|
||
|
|
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
|