2016-05-27 15:57:35 +00:00
|
|
|
// 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.
|
|
|
|
|
2019-05-24 13:51:59 +00:00
|
|
|
#include "src/init/v8.h"
|
2016-05-27 15:57:35 +00:00
|
|
|
|
2016-06-03 14:52:59 +00:00
|
|
|
#include "src/interpreter/bytecode-label.h"
|
2016-05-27 15:57:35 +00:00
|
|
|
#include "src/interpreter/bytecode-register-optimizer.h"
|
2017-12-13 12:19:44 +00:00
|
|
|
#include "test/unittests/interpreter/bytecode-utils.h"
|
2016-05-27 15:57:35 +00:00
|
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace interpreter {
|
|
|
|
|
2017-04-07 10:32:14 +00:00
|
|
|
class BytecodeRegisterOptimizerTest
|
|
|
|
: public BytecodeRegisterOptimizer::BytecodeWriter,
|
|
|
|
public TestWithIsolateAndZone {
|
2016-05-27 15:57:35 +00:00
|
|
|
public:
|
2017-04-07 10:32:14 +00:00
|
|
|
struct RegisterTransfer {
|
|
|
|
Bytecode bytecode;
|
|
|
|
Register input;
|
|
|
|
Register output;
|
|
|
|
};
|
|
|
|
|
2018-09-13 09:22:50 +00:00
|
|
|
BytecodeRegisterOptimizerTest() = default;
|
2016-05-27 15:57:35 +00:00
|
|
|
~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
|
|
|
|
|
|
|
|
void Initialize(int number_of_parameters, int number_of_locals) {
|
2016-09-30 09:02:59 +00:00
|
|
|
register_allocator_ = new BytecodeRegisterAllocator(number_of_locals);
|
|
|
|
register_optimizer_ = new (zone())
|
|
|
|
BytecodeRegisterOptimizer(zone(), register_allocator_, number_of_locals,
|
|
|
|
number_of_parameters, this);
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
2017-04-07 10:32:14 +00:00
|
|
|
void EmitLdar(Register input) override {
|
|
|
|
output_.push_back({Bytecode::kLdar, input, Register()});
|
|
|
|
}
|
|
|
|
void EmitStar(Register output) override {
|
|
|
|
output_.push_back({Bytecode::kStar, Register(), output});
|
2016-06-03 14:52:59 +00:00
|
|
|
}
|
2017-04-07 10:32:14 +00:00
|
|
|
void EmitMov(Register input, Register output) override {
|
|
|
|
output_.push_back({Bytecode::kMov, input, output});
|
2016-06-03 14:52:59 +00:00
|
|
|
}
|
2016-05-27 15:57:35 +00:00
|
|
|
|
2016-09-30 09:02:59 +00:00
|
|
|
BytecodeRegisterAllocator* allocator() { return register_allocator_; }
|
2016-05-27 15:57:35 +00:00
|
|
|
BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
|
|
|
|
|
2016-09-30 09:02:59 +00:00
|
|
|
Register NewTemporary() { return allocator()->NewRegister(); }
|
2016-05-27 15:57:35 +00:00
|
|
|
|
2016-09-30 09:02:59 +00:00
|
|
|
void ReleaseTemporaries(Register reg) {
|
|
|
|
allocator()->ReleaseRegisters(reg.index());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t write_count() const { return output_.size(); }
|
2017-04-07 10:32:14 +00:00
|
|
|
const RegisterTransfer& last_written() const { return output_.back(); }
|
|
|
|
const std::vector<RegisterTransfer>* output() { return &output_; }
|
2016-05-27 15:57:35 +00:00
|
|
|
|
|
|
|
private:
|
2016-09-30 09:02:59 +00:00
|
|
|
BytecodeRegisterAllocator* register_allocator_;
|
2016-05-27 15:57:35 +00:00
|
|
|
BytecodeRegisterOptimizer* register_optimizer_;
|
|
|
|
|
2017-04-07 10:32:14 +00:00
|
|
|
std::vector<RegisterTransfer> output_;
|
2016-05-27 15:57:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Sanity tests.
|
|
|
|
|
2016-10-24 20:47:41 +00:00
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForFlush) {
|
2016-05-27 15:57:35 +00:00
|
|
|
Initialize(1, 1);
|
2016-06-03 14:52:59 +00:00
|
|
|
Register temp = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-10-24 20:47:41 +00:00
|
|
|
optimizer()->Flush();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
|
|
|
|
CHECK_EQ(output()->at(0).output.index(), temp.index());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 20:47:41 +00:00
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
|
2016-05-27 15:57:35 +00:00
|
|
|
Initialize(1, 1);
|
2016-06-03 14:52:59 +00:00
|
|
|
Register temp = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kJump, AccumulatorUse::kNone>();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
|
|
|
|
CHECK_EQ(output()->at(0).output.index(), temp.index());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Basic Register Optimizations
|
|
|
|
|
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
|
|
|
|
Initialize(3, 1);
|
|
|
|
Register parameter = Register::FromParameterIndex(1, 3);
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoLdar(parameter);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-05-27 15:57:35 +00:00
|
|
|
Register temp = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp);
|
2016-09-30 09:02:59 +00:00
|
|
|
ReleaseTemporaries(temp);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(0).bytecode, Bytecode::kLdar);
|
|
|
|
CHECK_EQ(output()->at(0).input.index(), parameter.index());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
2016-09-30 09:02:59 +00:00
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterUsed) {
|
|
|
|
Initialize(3, 1);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
|
2016-09-30 09:02:59 +00:00
|
|
|
Register temp0 = NewTemporary();
|
|
|
|
Register temp1 = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp1);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
|
|
|
|
CHECK_EQ(output()->at(0).output.index(), temp1.index());
|
|
|
|
optimizer()->DoMov(temp1, temp0);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2016-09-30 09:02:59 +00:00
|
|
|
ReleaseTemporaries(temp1);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoLdar(temp0);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 2u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar);
|
|
|
|
CHECK_EQ(output()->at(1).input.index(), temp1.index());
|
2016-09-30 09:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterNotFlushed) {
|
|
|
|
Initialize(3, 1);
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
|
2016-09-30 09:02:59 +00:00
|
|
|
Register temp0 = NewTemporary();
|
|
|
|
Register temp1 = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp0);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp1);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-09-30 09:02:59 +00:00
|
|
|
ReleaseTemporaries(temp1);
|
2016-10-24 20:47:41 +00:00
|
|
|
optimizer()->Flush();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
|
|
|
|
CHECK_EQ(output()->at(0).output.index(), temp0.index());
|
2016-09-30 09:02:59 +00:00
|
|
|
}
|
|
|
|
|
2016-05-27 15:57:35 +00:00
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
|
|
|
|
Initialize(3, 1);
|
|
|
|
Register parameter = Register::FromParameterIndex(1, 3);
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoLdar(parameter);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-05-27 15:57:35 +00:00
|
|
|
Register local = Register(0);
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(local);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 1u);
|
2017-04-07 10:32:14 +00:00
|
|
|
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());
|
2016-05-27 15:57:35 +00:00
|
|
|
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 2u);
|
2017-04-07 10:32:14 +00:00
|
|
|
CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar);
|
|
|
|
CHECK_EQ(output()->at(1).input.index(), local.index());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 20:47:41 +00:00
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, SingleTemporaryNotMaterializedForInput) {
|
2016-05-27 15:57:35 +00:00
|
|
|
Initialize(3, 1);
|
|
|
|
Register parameter = Register::FromParameterIndex(1, 3);
|
|
|
|
Register temp0 = NewTemporary();
|
|
|
|
Register temp1 = NewTemporary();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoMov(parameter, temp0);
|
|
|
|
optimizer()->DoMov(parameter, temp1);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-10-24 20:47:41 +00:00
|
|
|
|
|
|
|
Register reg = optimizer()->GetInputRegister(temp0);
|
2017-12-13 12:19:44 +00:00
|
|
|
RegisterList reg_list = optimizer()->GetInputRegisterList(
|
|
|
|
BytecodeUtils::NewRegisterList(temp0.index(), 1));
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-10-24 20:47:41 +00:00
|
|
|
CHECK_EQ(parameter.index(), reg.index());
|
|
|
|
CHECK_EQ(parameter.index(), reg_list.first_register().index());
|
|
|
|
CHECK_EQ(1, reg_list.register_count());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(BytecodeRegisterOptimizerTest, RangeOfTemporariesMaterializedForInput) {
|
|
|
|
Initialize(3, 1);
|
|
|
|
Register parameter = Register::FromParameterIndex(1, 3);
|
|
|
|
Register temp0 = NewTemporary();
|
|
|
|
Register temp1 = NewTemporary();
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
|
2017-04-07 10:32:14 +00:00
|
|
|
optimizer()->DoStar(temp0);
|
|
|
|
optimizer()->DoMov(parameter, temp1);
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 0u);
|
2016-05-27 15:57:35 +00:00
|
|
|
|
2016-12-15 07:55:10 +00:00
|
|
|
optimizer()
|
|
|
|
->PrepareForBytecode<Bytecode::kCallJSRuntime, AccumulatorUse::kWrite>();
|
2017-12-13 12:19:44 +00:00
|
|
|
RegisterList reg_list = optimizer()->GetInputRegisterList(
|
|
|
|
BytecodeUtils::NewRegisterList(temp0.index(), 2));
|
2016-10-24 20:47:41 +00:00
|
|
|
CHECK_EQ(temp0.index(), reg_list.first_register().index());
|
|
|
|
CHECK_EQ(2, reg_list.register_count());
|
2016-11-11 12:12:31 +00:00
|
|
|
CHECK_EQ(write_count(), 2u);
|
2017-04-07 10:32:14 +00:00
|
|
|
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());
|
2016-05-27 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace interpreter
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|