blob: 17ab8e190c840958d52abf340033d095c20360fa [file] [log] [blame]
// 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/factory.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-optimizer.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
public TestWithIsolateAndZone {
public:
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 Write(BytecodeNode* node) override { output_.push_back(*node); }
void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
output_.push_back(*node);
}
void BindLabel(BytecodeLabel* label) override {}
void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
Handle<BytecodeArray> ToBytecodeArray(
Isolate* isolate, int fixed_register_count, int parameter_count,
Handle<FixedArray> handle_table) override {
return Handle<BytecodeArray>();
}
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 BytecodeNode& last_written() const { return output_.back(); }
const std::vector<BytecodeNode>* output() { return &output_; }
private:
BytecodeRegisterAllocator* register_allocator_;
BytecodeRegisterOptimizer* register_optimizer_;
std::vector<BytecodeNode> output_;
};
// Sanity tests.
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForFlush) {
Initialize(1, 1);
Register temp = NewTemporary();
optimizer()->DoStar(temp, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
optimizer()->Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
}
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
Initialize(1, 1);
Register temp = NewTemporary();
optimizer()->DoStar(temp, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
optimizer()->PrepareForBytecode(Bytecode::kJump);
CHECK_EQ(write_count(), 1);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
}
// Basic Register Optimizations
TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
Initialize(3, 1);
Register parameter = Register::FromParameterIndex(1, 3);
optimizer()->DoLdar(parameter, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
Register temp = NewTemporary();
optimizer()->DoStar(temp, BytecodeSourceInfo());
BytecodeNode node1(Bytecode::kStar, NewTemporary().ToOperand());
ReleaseTemporaries(temp);
CHECK_EQ(write_count(), 0);
optimizer()->PrepareForBytecode(Bytecode::kReturn);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kLdar);
CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
}
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterUsed) {
Initialize(3, 1);
optimizer()->PrepareForBytecode(Bytecode::kLdaSmi);
Register temp0 = NewTemporary();
Register temp1 = NewTemporary();
optimizer()->DoStar(temp1, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
optimizer()->PrepareForBytecode(Bytecode::kLdaSmi);
CHECK_EQ(write_count(), 1);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp1.ToOperand());
optimizer()->DoMov(temp1, temp0, BytecodeSourceInfo());
CHECK_EQ(write_count(), 1);
ReleaseTemporaries(temp1);
CHECK_EQ(write_count(), 1);
optimizer()->DoLdar(temp0, BytecodeSourceInfo());
CHECK_EQ(write_count(), 1);
optimizer()->PrepareForBytecode(Bytecode::kReturn);
CHECK_EQ(write_count(), 2);
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kLdar);
CHECK_EQ(output()->at(1).operand(0), temp1.ToOperand());
}
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterNotFlushed) {
Initialize(3, 1);
optimizer()->PrepareForBytecode(Bytecode::kLdaSmi);
Register temp0 = NewTemporary();
Register temp1 = NewTemporary();
optimizer()->DoStar(temp0, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
optimizer()->DoStar(temp1, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
ReleaseTemporaries(temp1);
optimizer()->Flush();
CHECK_EQ(write_count(), 1);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp0.ToOperand());
}
TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
Initialize(3, 1);
Register parameter = Register::FromParameterIndex(1, 3);
optimizer()->DoLdar(parameter, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
Register local = Register(0);
optimizer()->DoStar(local, BytecodeSourceInfo());
CHECK_EQ(write_count(), 1);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kMov);
CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
CHECK_EQ(output()->at(0).operand(1), local.ToOperand());
optimizer()->PrepareForBytecode(Bytecode::kReturn);
CHECK_EQ(write_count(), 2);
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kLdar);
CHECK_EQ(output()->at(1).operand(0), local.ToOperand());
}
TEST_F(BytecodeRegisterOptimizerTest, SingleTemporaryNotMaterializedForInput) {
Initialize(3, 1);
Register parameter = Register::FromParameterIndex(1, 3);
Register temp0 = NewTemporary();
Register temp1 = NewTemporary();
optimizer()->DoMov(parameter, temp0, BytecodeSourceInfo());
optimizer()->DoMov(parameter, temp1, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
Register reg = optimizer()->GetInputRegister(temp0);
RegisterList reg_list =
optimizer()->GetInputRegisterList(RegisterList(temp0.index(), 1));
CHECK_EQ(write_count(), 0);
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);
optimizer()->DoStar(temp0, BytecodeSourceInfo());
optimizer()->DoMov(parameter, temp1, BytecodeSourceInfo());
CHECK_EQ(write_count(), 0);
optimizer()->PrepareForBytecode(Bytecode::kCallJSRuntime);
RegisterList reg_list =
optimizer()->GetInputRegisterList(RegisterList(temp0.index(), 2));
CHECK_EQ(temp0.index(), reg_list.first_register().index());
CHECK_EQ(2, reg_list.register_count());
CHECK_EQ(write_count(), 2);
CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
CHECK_EQ(output()->at(0).operand(0), temp0.ToOperand());
CHECK_EQ(output()->at(1).bytecode(), Bytecode::kMov);
CHECK_EQ(output()->at(1).operand(0), parameter.ToOperand());
CHECK_EQ(output()->at(1).operand(1), temp1.ToOperand());
}
} // namespace interpreter
} // namespace internal
} // namespace v8