blob: 795bee822131496da1f954b15f7a8919560bd816 [file] [log] [blame]
Ben Murdoch61f157c2016-09-16 13:49:30 +01001// 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/factory.h"
8#include "src/interpreter/bytecode-label.h"
9#include "src/interpreter/bytecode-register-optimizer.h"
10#include "src/objects-inl.h"
11#include "src/objects.h"
12#include "test/unittests/test-utils.h"
13
14namespace v8 {
15namespace internal {
16namespace interpreter {
17
18class BytecodeRegisterOptimizerTest : public BytecodePipelineStage,
19 public TestWithIsolateAndZone {
20 public:
21 BytecodeRegisterOptimizerTest() {}
22 ~BytecodeRegisterOptimizerTest() override { delete register_allocator_; }
23
24 void Initialize(int number_of_parameters, int number_of_locals) {
25 register_allocator_ =
26 new TemporaryRegisterAllocator(zone(), number_of_locals);
27 register_optimizer_ = new (zone()) BytecodeRegisterOptimizer(
28 zone(), register_allocator_, number_of_parameters, this);
29 }
30
31 void Write(BytecodeNode* node) override { output_.push_back(*node); }
32 void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
33 output_.push_back(*node);
34 }
35 void BindLabel(BytecodeLabel* label) override {}
36 void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
37 Handle<BytecodeArray> ToBytecodeArray(
38 int fixed_register_count, int parameter_count,
39 Handle<FixedArray> handle_table) override {
40 return Handle<BytecodeArray>();
41 }
42
43 TemporaryRegisterAllocator* allocator() { return register_allocator_; }
44 BytecodeRegisterOptimizer* optimizer() { return register_optimizer_; }
45
46 Register NewTemporary() {
47 return Register(allocator()->BorrowTemporaryRegister());
48 }
49
50 void KillTemporary(Register reg) {
51 allocator()->ReturnTemporaryRegister(reg.index());
52 }
53
54 size_t write_count() const { return output_.size(); }
55 const BytecodeNode& last_written() const { return output_.back(); }
56 const std::vector<BytecodeNode>* output() { return &output_; }
57
58 private:
59 TemporaryRegisterAllocator* register_allocator_;
60 BytecodeRegisterOptimizer* register_optimizer_;
61
62 std::vector<BytecodeNode> output_;
63};
64
65// Sanity tests.
66
67TEST_F(BytecodeRegisterOptimizerTest, WriteNop) {
68 Initialize(1, 1);
69 BytecodeNode node(Bytecode::kNop);
70 optimizer()->Write(&node);
71 CHECK_EQ(write_count(), 1);
72 CHECK_EQ(node, last_written());
73}
74
75TEST_F(BytecodeRegisterOptimizerTest, WriteNopExpression) {
76 Initialize(1, 1);
77 BytecodeNode node(Bytecode::kNop);
78 node.source_info().MakeExpressionPosition(3);
79 optimizer()->Write(&node);
80 CHECK_EQ(write_count(), 1);
81 CHECK_EQ(node, last_written());
82}
83
84TEST_F(BytecodeRegisterOptimizerTest, WriteNopStatement) {
85 Initialize(1, 1);
86 BytecodeNode node(Bytecode::kNop);
87 node.source_info().MakeStatementPosition(3);
88 optimizer()->Write(&node);
89 CHECK_EQ(write_count(), 1);
90 CHECK_EQ(node, last_written());
91}
92
93TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForJump) {
94 Initialize(1, 1);
95 Register temp = NewTemporary();
96 BytecodeNode node(Bytecode::kStar, temp.ToOperand());
97 optimizer()->Write(&node);
98 CHECK_EQ(write_count(), 0);
99 BytecodeLabel label;
100 BytecodeNode jump(Bytecode::kJump, 0);
101 optimizer()->WriteJump(&jump, &label);
102 CHECK_EQ(write_count(), 2);
103 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
104 CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
105 CHECK_EQ(output()->at(1).bytecode(), Bytecode::kJump);
106}
107
108TEST_F(BytecodeRegisterOptimizerTest, TemporaryMaterializedForBind) {
109 Initialize(1, 1);
110 Register temp = NewTemporary();
111 BytecodeNode node(Bytecode::kStar, temp.ToOperand());
112 optimizer()->Write(&node);
113 CHECK_EQ(write_count(), 0);
114 BytecodeLabel label;
115 optimizer()->BindLabel(&label);
116 CHECK_EQ(write_count(), 1);
117 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kStar);
118 CHECK_EQ(output()->at(0).operand(0), temp.ToOperand());
119}
120
121// Basic Register Optimizations
122
123TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotEmitted) {
124 Initialize(3, 1);
125 Register parameter = Register::FromParameterIndex(1, 3);
126 BytecodeNode node0(Bytecode::kLdar, parameter.ToOperand());
127 optimizer()->Write(&node0);
128 CHECK_EQ(write_count(), 0);
129 Register temp = NewTemporary();
130 BytecodeNode node1(Bytecode::kStar, NewTemporary().ToOperand());
131 optimizer()->Write(&node1);
132 CHECK_EQ(write_count(), 0);
133 KillTemporary(temp);
134 CHECK_EQ(write_count(), 0);
135 BytecodeNode node2(Bytecode::kReturn);
136 optimizer()->Write(&node2);
137 CHECK_EQ(write_count(), 2);
138 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kLdar);
139 CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
140 CHECK_EQ(output()->at(1).bytecode(), Bytecode::kReturn);
141}
142
143TEST_F(BytecodeRegisterOptimizerTest, StoresToLocalsImmediate) {
144 Initialize(3, 1);
145 Register parameter = Register::FromParameterIndex(1, 3);
146 BytecodeNode node0(Bytecode::kLdar, parameter.ToOperand());
147 optimizer()->Write(&node0);
148 CHECK_EQ(write_count(), 0);
149 Register local = Register(0);
150 BytecodeNode node1(Bytecode::kStar, local.ToOperand());
151 optimizer()->Write(&node1);
152 CHECK_EQ(write_count(), 1);
153 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kMov);
154 CHECK_EQ(output()->at(0).operand(0), parameter.ToOperand());
155 CHECK_EQ(output()->at(0).operand(1), local.ToOperand());
156
157 BytecodeNode node2(Bytecode::kReturn);
158 optimizer()->Write(&node2);
159 CHECK_EQ(write_count(), 3);
160 CHECK_EQ(output()->at(1).bytecode(), Bytecode::kLdar);
161 CHECK_EQ(output()->at(1).operand(0), local.ToOperand());
162 CHECK_EQ(output()->at(2).bytecode(), Bytecode::kReturn);
163}
164
165TEST_F(BytecodeRegisterOptimizerTest, TemporaryNotMaterializedForInput) {
166 Initialize(3, 1);
167 Register parameter = Register::FromParameterIndex(1, 3);
168 Register temp0 = NewTemporary();
169 Register temp1 = NewTemporary();
170 BytecodeNode node0(Bytecode::kMov, parameter.ToOperand(), temp0.ToOperand());
171 optimizer()->Write(&node0);
172 BytecodeNode node1(Bytecode::kMov, parameter.ToOperand(), temp1.ToOperand());
173 optimizer()->Write(&node1);
174 CHECK_EQ(write_count(), 0);
175 BytecodeNode node2(Bytecode::kCallJSRuntime, 0, temp0.ToOperand(), 1);
176 optimizer()->Write(&node2);
177 CHECK_EQ(write_count(), 1);
178 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kCallJSRuntime);
179 CHECK_EQ(output()->at(0).operand(0), 0);
180 CHECK_EQ(output()->at(0).operand(1), parameter.ToOperand());
181 CHECK_EQ(output()->at(0).operand(2), 1);
182}
183
184TEST_F(BytecodeRegisterOptimizerTest, RangeOfTemporariesMaterializedForInput) {
185 Initialize(3, 1);
186 Register parameter = Register::FromParameterIndex(1, 3);
187 Register temp0 = NewTemporary();
188 Register temp1 = NewTemporary();
189 BytecodeNode node0(Bytecode::kLdaSmi, 3);
190 optimizer()->Write(&node0);
191 CHECK_EQ(write_count(), 1);
192 BytecodeNode node1(Bytecode::kStar, temp0.ToOperand());
193 optimizer()->Write(&node1);
194 BytecodeNode node2(Bytecode::kMov, parameter.ToOperand(), temp1.ToOperand());
195 optimizer()->Write(&node2);
196 CHECK_EQ(write_count(), 1);
197 BytecodeNode node3(Bytecode::kCallJSRuntime, 0, temp0.ToOperand(), 2);
198 optimizer()->Write(&node3);
199 CHECK_EQ(write_count(), 4);
200
201 CHECK_EQ(output()->at(0).bytecode(), Bytecode::kLdaSmi);
202 CHECK_EQ(output()->at(0).operand(0), 3);
203
204 CHECK_EQ(output()->at(1).bytecode(), Bytecode::kStar);
205 CHECK_EQ(output()->at(1).operand(0), temp0.ToOperand());
206
207 CHECK_EQ(output()->at(2).bytecode(), Bytecode::kMov);
208 CHECK_EQ(output()->at(2).operand(0), parameter.ToOperand());
209 CHECK_EQ(output()->at(2).operand(1), temp1.ToOperand());
210
211 CHECK_EQ(output()->at(3).bytecode(), Bytecode::kCallJSRuntime);
212 CHECK_EQ(output()->at(3).operand(0), 0);
213 CHECK_EQ(output()->at(3).operand(1), temp0.ToOperand());
214 CHECK_EQ(output()->at(3).operand(2), 2);
215}
216
217} // namespace interpreter
218} // namespace internal
219} // namespace v8