Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/v8.h"
6 :
7 : #include "src/interpreter/bytecode-label.h"
8 : #include "src/interpreter/bytecode-register-optimizer.h"
9 : #include "test/unittests/interpreter/bytecode-utils.h"
10 : #include "test/unittests/test-utils.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 : namespace interpreter {
15 :
16 : class BytecodeRegisterOptimizerTest
17 : : public BytecodeRegisterOptimizer::BytecodeWriter,
18 : public TestWithIsolateAndZone {
19 : public:
20 : struct RegisterTransfer {
21 : Bytecode bytecode;
22 : Register input;
23 : Register output;
24 : };
25 :
26 8 : BytecodeRegisterOptimizerTest() = default;
27 8 : ~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
28 :
29 8 : void Initialize(int number_of_parameters, int number_of_locals) {
30 16 : register_allocator_ = new BytecodeRegisterAllocator(number_of_locals);
31 : register_optimizer_ = new (zone())
32 : BytecodeRegisterOptimizer(zone(), register_allocator_, number_of_locals,
33 8 : number_of_parameters, this);
34 8 : }
35 :
36 3 : void EmitLdar(Register input) override {
37 9 : output_.push_back({Bytecode::kLdar, input, Register()});
38 3 : }
39 5 : void EmitStar(Register output) override {
40 15 : output_.push_back({Bytecode::kStar, Register(), output});
41 5 : }
42 2 : void EmitMov(Register input, Register output) override {
43 4 : output_.push_back({Bytecode::kMov, input, output});
44 2 : }
45 :
46 : BytecodeRegisterAllocator* allocator() { return register_allocator_; }
47 : BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
48 :
49 7 : Register NewTemporary() { return allocator()->NewRegister(); }
50 :
51 : void ReleaseTemporaries(Register reg) {
52 : allocator()->ReleaseRegisters(reg.index());
53 : }
54 :
55 : size_t write_count() const { return output_.size(); }
56 : const RegisterTransfer& last_written() const { return output_.back(); }
57 : const std::vector<RegisterTransfer>* output() { return &output_; }
58 :
59 : private:
60 : BytecodeRegisterAllocator* register_allocator_;
61 : BytecodeRegisterOptimizer* register_optimizer_;
62 :
63 : std::vector<RegisterTransfer> output_;
64 : };
65 :
66 : // Sanity tests.
67 :
68 15418 : TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForFlush) {
69 1 : Initialize(1, 1);
70 : Register temp = NewTemporary();
71 : optimizer()->DoStar(temp);
72 1 : CHECK_EQ(write_count(), 0u);
73 1 : optimizer()->Flush();
74 1 : CHECK_EQ(write_count(), 1u);
75 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
76 1 : CHECK_EQ(output()->at(0).output.index(), temp.index());
77 1 : }
78 :
79 15418 : TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
80 1 : Initialize(1, 1);
81 : Register temp = NewTemporary();
82 : optimizer()->DoStar(temp);
83 1 : CHECK_EQ(write_count(), 0u);
84 : optimizer()->PrepareForBytecode<Bytecode::kJump, AccumulatorUse::kNone>();
85 1 : CHECK_EQ(write_count(), 1u);
86 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
87 1 : CHECK_EQ(output()->at(0).output.index(), temp.index());
88 1 : }
89 :
90 : // Basic Register Optimizations
91 :
92 15418 : TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
93 1 : Initialize(3, 1);
94 1 : Register parameter = Register::FromParameterIndex(1, 3);
95 : optimizer()->DoLdar(parameter);
96 1 : CHECK_EQ(write_count(), 0u);
97 : Register temp = NewTemporary();
98 : optimizer()->DoStar(temp);
99 : ReleaseTemporaries(temp);
100 1 : CHECK_EQ(write_count(), 0u);
101 : optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
102 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kLdar);
103 1 : CHECK_EQ(output()->at(0).input.index(), parameter.index());
104 1 : }
105 :
106 15418 : TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterUsed) {
107 1 : Initialize(3, 1);
108 : optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
109 : Register temp0 = NewTemporary();
110 : Register temp1 = NewTemporary();
111 : optimizer()->DoStar(temp1);
112 1 : CHECK_EQ(write_count(), 0u);
113 : optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
114 1 : CHECK_EQ(write_count(), 1u);
115 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
116 1 : CHECK_EQ(output()->at(0).output.index(), temp1.index());
117 1 : optimizer()->DoMov(temp1, temp0);
118 1 : CHECK_EQ(write_count(), 1u);
119 : ReleaseTemporaries(temp1);
120 1 : CHECK_EQ(write_count(), 1u);
121 : optimizer()->DoLdar(temp0);
122 1 : CHECK_EQ(write_count(), 1u);
123 : optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
124 1 : CHECK_EQ(write_count(), 2u);
125 1 : CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar);
126 1 : CHECK_EQ(output()->at(1).input.index(), temp1.index());
127 1 : }
128 :
129 15418 : TEST_F(BytecodeRegisterOptimizerTest, ReleasedRegisterNotFlushed) {
130 1 : Initialize(3, 1);
131 : optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
132 : Register temp0 = NewTemporary();
133 : Register temp1 = NewTemporary();
134 : optimizer()->DoStar(temp0);
135 1 : CHECK_EQ(write_count(), 0u);
136 : optimizer()->DoStar(temp1);
137 1 : CHECK_EQ(write_count(), 0u);
138 : ReleaseTemporaries(temp1);
139 1 : optimizer()->Flush();
140 1 : CHECK_EQ(write_count(), 1u);
141 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
142 1 : CHECK_EQ(output()->at(0).output.index(), temp0.index());
143 1 : }
144 :
145 15418 : TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
146 1 : Initialize(3, 1);
147 1 : Register parameter = Register::FromParameterIndex(1, 3);
148 : optimizer()->DoLdar(parameter);
149 1 : CHECK_EQ(write_count(), 0u);
150 : Register local = Register(0);
151 : optimizer()->DoStar(local);
152 1 : CHECK_EQ(write_count(), 1u);
153 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kMov);
154 1 : CHECK_EQ(output()->at(0).input.index(), parameter.index());
155 1 : CHECK_EQ(output()->at(0).output.index(), local.index());
156 :
157 : optimizer()->PrepareForBytecode<Bytecode::kReturn, AccumulatorUse::kRead>();
158 1 : CHECK_EQ(write_count(), 2u);
159 1 : CHECK_EQ(output()->at(1).bytecode, Bytecode::kLdar);
160 1 : CHECK_EQ(output()->at(1).input.index(), local.index());
161 1 : }
162 :
163 15418 : TEST_F(BytecodeRegisterOptimizerTest, SingleTemporaryNotMaterializedForInput) {
164 1 : Initialize(3, 1);
165 1 : Register parameter = Register::FromParameterIndex(1, 3);
166 : Register temp0 = NewTemporary();
167 1 : Register temp1 = NewTemporary();
168 1 : optimizer()->DoMov(parameter, temp0);
169 1 : optimizer()->DoMov(parameter, temp1);
170 1 : CHECK_EQ(write_count(), 0u);
171 :
172 1 : Register reg = optimizer()->GetInputRegister(temp0);
173 : RegisterList reg_list = optimizer()->GetInputRegisterList(
174 1 : BytecodeUtils::NewRegisterList(temp0.index(), 1));
175 1 : CHECK_EQ(write_count(), 0u);
176 1 : CHECK_EQ(parameter.index(), reg.index());
177 1 : CHECK_EQ(parameter.index(), reg_list.first_register().index());
178 1 : CHECK_EQ(1, reg_list.register_count());
179 1 : }
180 :
181 15418 : TEST_F(BytecodeRegisterOptimizerTest, RangeOfTemporariesMaterializedForInput) {
182 1 : Initialize(3, 1);
183 1 : Register parameter = Register::FromParameterIndex(1, 3);
184 : Register temp0 = NewTemporary();
185 : Register temp1 = NewTemporary();
186 : optimizer()->PrepareForBytecode<Bytecode::kLdaSmi, AccumulatorUse::kWrite>();
187 : optimizer()->DoStar(temp0);
188 1 : optimizer()->DoMov(parameter, temp1);
189 1 : CHECK_EQ(write_count(), 0u);
190 :
191 : optimizer()
192 : ->PrepareForBytecode<Bytecode::kCallJSRuntime, AccumulatorUse::kWrite>();
193 : RegisterList reg_list = optimizer()->GetInputRegisterList(
194 1 : BytecodeUtils::NewRegisterList(temp0.index(), 2));
195 1 : CHECK_EQ(temp0.index(), reg_list.first_register().index());
196 1 : CHECK_EQ(2, reg_list.register_count());
197 1 : CHECK_EQ(write_count(), 2u);
198 1 : CHECK_EQ(output()->at(0).bytecode, Bytecode::kStar);
199 1 : CHECK_EQ(output()->at(0).output.index(), temp0.index());
200 1 : CHECK_EQ(output()->at(1).bytecode, Bytecode::kMov);
201 1 : CHECK_EQ(output()->at(1).input.index(), parameter.index());
202 1 : CHECK_EQ(output()->at(1).output.index(), temp1.index());
203 1 : }
204 :
205 : } // namespace interpreter
206 : } // namespace internal
207 9249 : } // namespace v8
|