v8/test/unittests/interpreter/bytecode-register-optimizer-unittest.cc
Ross McIlroy 1d1f52534e [Interpreter] Make RegisterList constructor private to avoid missue.
RegisterLists should only be allocated via the register allocator. To ensure
this, make the RegisterList constructor private and only expose it to tests
and the BytecodeRegisterAllocator.

Change-Id: I09ebfc5c0f1baecfb1333fd672b96d462fd26fcf
Reviewed-on: https://chromium-review.googlesource.com/822196
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50073}
2017-12-13 13:15:35 +00:00

208 lines
7.3 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/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() {}
~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
void Initialize(int number_of_parameters, int number_of_locals) {
register_allocator_ = new BytecodeRegisterAllocator(number_of_locals);
register_optimizer_ = new (zone())
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, AccumulatorUse::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, 3);
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, AccumulatorUse::kRead>();
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, AccumulatorUse::kWrite>();
Register temp0 = NewTemporary();
Register temp1 = NewTemporary();
optimizer()->DoStar(temp1);
CHECK_EQ(write_count(), 0u);
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
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, AccumulatorUse::kRead>();
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, AccumulatorUse::kWrite>();
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, 3);
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, AccumulatorUse::kRead>();
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, 3);
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, 3);
Register temp0 = NewTemporary();
Register temp1 = NewTemporary();
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
optimizer()->DoStar(temp0);
optimizer()->DoMov(parameter, temp1);
CHECK_EQ(write_count(), 0u);
optimizer()
->PrepareForBytecode<Bytecode::kCallJSRuntime, AccumulatorUse::kWrite>();
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