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/factory.h"
8#include "src/interpreter/bytecode-label.h"
9#include "src/interpreter/bytecode-peephole-optimizer.h"
10#include "src/interpreter/constant-array-builder.h"
11#include "src/objects-inl.h"
12#include "src/objects.h"
13#include "test/unittests/test-utils.h"
14
15namespace v8 {
16namespace internal {
17namespace interpreter {
18
19class BytecodePeepholeOptimizerTest : public BytecodePipelineStage,
20                                      public TestWithIsolateAndZone {
21 public:
22  BytecodePeepholeOptimizerTest()
23      : constant_array_builder_(isolate(), zone()),
24        peephole_optimizer_(&constant_array_builder_, this) {}
25  ~BytecodePeepholeOptimizerTest() override {}
26
27  void Write(BytecodeNode* node) override {
28    write_count_++;
29    last_written_.Clone(node);
30  }
31
32  void WriteJump(BytecodeNode* node, BytecodeLabel* label) override {
33    write_count_++;
34    last_written_.Clone(node);
35  }
36
37  void BindLabel(BytecodeLabel* label) override {}
38  void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {}
39  Handle<BytecodeArray> ToBytecodeArray(
40      int fixed_register_count, int parameter_count,
41      Handle<FixedArray> handle_table) override {
42    return Handle<BytecodeArray>();
43  }
44
45  void Flush() {
46    optimizer()->ToBytecodeArray(0, 0, factory()->empty_fixed_array());
47  }
48
49  BytecodePeepholeOptimizer* optimizer() { return &peephole_optimizer_; }
50  ConstantArrayBuilder* constant_array() { return &constant_array_builder_; }
51
52  int write_count() const { return write_count_; }
53  const BytecodeNode& last_written() const { return last_written_; }
54
55 private:
56  ConstantArrayBuilder constant_array_builder_;
57  BytecodePeepholeOptimizer peephole_optimizer_;
58
59  int write_count_ = 0;
60  BytecodeNode last_written_;
61};
62
63// Sanity tests.
64
65TEST_F(BytecodePeepholeOptimizerTest, FlushOnJump) {
66  CHECK_EQ(write_count(), 0);
67
68  BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
69  optimizer()->Write(&add);
70  CHECK_EQ(write_count(), 0);
71
72  BytecodeLabel target;
73  BytecodeNode jump(Bytecode::kJump, 0);
74  optimizer()->WriteJump(&jump, &target);
75  CHECK_EQ(write_count(), 2);
76  CHECK_EQ(jump, last_written());
77}
78
79TEST_F(BytecodePeepholeOptimizerTest, FlushOnBind) {
80  CHECK_EQ(write_count(), 0);
81
82  BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
83  optimizer()->Write(&add);
84  CHECK_EQ(write_count(), 0);
85
86  BytecodeLabel target;
87  optimizer()->BindLabel(&target);
88  CHECK_EQ(write_count(), 1);
89  CHECK_EQ(add, last_written());
90}
91
92// Nop elimination tests.
93
94TEST_F(BytecodePeepholeOptimizerTest, ElideEmptyNop) {
95  BytecodeNode nop(Bytecode::kNop);
96  optimizer()->Write(&nop);
97  BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
98  optimizer()->Write(&add);
99  Flush();
100  CHECK_EQ(write_count(), 1);
101  CHECK_EQ(add, last_written());
102}
103
104TEST_F(BytecodePeepholeOptimizerTest, ElideExpressionNop) {
105  BytecodeNode nop(Bytecode::kNop);
106  nop.source_info().MakeExpressionPosition(3);
107  optimizer()->Write(&nop);
108  BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
109  optimizer()->Write(&add);
110  Flush();
111  CHECK_EQ(write_count(), 1);
112  CHECK_EQ(add, last_written());
113}
114
115TEST_F(BytecodePeepholeOptimizerTest, KeepStatementNop) {
116  BytecodeNode nop(Bytecode::kNop);
117  nop.source_info().MakeStatementPosition(3);
118  optimizer()->Write(&nop);
119  BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand());
120  add.source_info().MakeExpressionPosition(3);
121  optimizer()->Write(&add);
122  Flush();
123  CHECK_EQ(write_count(), 2);
124  CHECK_EQ(add, last_written());
125}
126
127// Tests covering BytecodePeepholeOptimizer::UpdateCurrentBytecode().
128
129TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) {
130  BytecodeNode first(Bytecode::kLdaNull);
131  BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3);
132  optimizer()->Write(&first);
133  CHECK_EQ(write_count(), 0);
134  optimizer()->Write(&second);
135  CHECK_EQ(write_count(), 1);
136  CHECK_EQ(last_written(), first);
137  Flush();
138  CHECK_EQ(write_count(), 2);
139  CHECK_EQ(last_written(), second);
140}
141
142TEST_F(BytecodePeepholeOptimizerTest, ElideJumpIfToBooleanTrue) {
143  BytecodeNode first(Bytecode::kLdaTrue);
144  BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3);
145  optimizer()->Write(&first);
146  CHECK_EQ(write_count(), 0);
147  optimizer()->Write(&second);
148  CHECK_EQ(write_count(), 1);
149  CHECK_EQ(last_written(), first);
150  Flush();
151  CHECK_EQ(write_count(), 2);
152  CHECK_EQ(last_written().bytecode(), Bytecode::kJumpIfTrue);
153  CHECK_EQ(last_written().operand(0), second.operand(0));
154}
155
156TEST_F(BytecodePeepholeOptimizerTest, KeepToBooleanLogicalNot) {
157  BytecodeNode first(Bytecode::kLdaNull);
158  BytecodeNode second(Bytecode::kToBooleanLogicalNot);
159  optimizer()->Write(&first);
160  CHECK_EQ(write_count(), 0);
161  optimizer()->Write(&second);
162  CHECK_EQ(write_count(), 1);
163  CHECK_EQ(last_written(), first);
164  Flush();
165  CHECK_EQ(write_count(), 2);
166  CHECK_EQ(last_written(), second);
167}
168
169TEST_F(BytecodePeepholeOptimizerTest, ElideToBooleanLogicalNot) {
170  BytecodeNode first(Bytecode::kLdaTrue);
171  BytecodeNode second(Bytecode::kToBooleanLogicalNot);
172  optimizer()->Write(&first);
173  CHECK_EQ(write_count(), 0);
174  optimizer()->Write(&second);
175  CHECK_EQ(write_count(), 1);
176  CHECK_EQ(last_written(), first);
177  Flush();
178  CHECK_EQ(write_count(), 2);
179  CHECK_EQ(last_written().bytecode(), Bytecode::kLogicalNot);
180}
181
182// Tests covering BytecodePeepholeOptimizer::CanElideCurrent().
183
184TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRy) {
185  BytecodeNode first(Bytecode::kStar, Register(0).ToOperand());
186  BytecodeNode second(Bytecode::kLdar, Register(1).ToOperand());
187  optimizer()->Write(&first);
188  CHECK_EQ(write_count(), 0);
189  optimizer()->Write(&second);
190  CHECK_EQ(write_count(), 1);
191  CHECK_EQ(last_written(), first);
192  Flush();
193  CHECK_EQ(write_count(), 2);
194  CHECK_EQ(last_written(), second);
195}
196
197TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRx) {
198  BytecodeLabel label;
199  BytecodeNode first(Bytecode::kStar, Register(0).ToOperand());
200  BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand());
201  optimizer()->Write(&first);
202  CHECK_EQ(write_count(), 0);
203  optimizer()->Write(&second);
204  CHECK_EQ(write_count(), 1);
205  CHECK_EQ(last_written(), first);
206  Flush();
207  CHECK_EQ(write_count(), 1);
208}
209
210TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatement) {
211  BytecodeNode first(Bytecode::kStar, Register(0).ToOperand());
212  BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand());
213  second.source_info().MakeStatementPosition(0);
214  optimizer()->Write(&first);
215  CHECK_EQ(write_count(), 0);
216  optimizer()->Write(&second);
217  CHECK_EQ(write_count(), 1);
218  CHECK_EQ(last_written(), first);
219  Flush();
220  CHECK_EQ(write_count(), 2);
221  CHECK_EQ(last_written().bytecode(), Bytecode::kNop);
222  CHECK_EQ(last_written().source_info(), second.source_info());
223}
224
225TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatementStarRy) {
226  BytecodeLabel label;
227  BytecodeNode first(Bytecode::kStar, Register(0).ToOperand());
228  BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand());
229  BytecodeNode third(Bytecode::kStar, Register(3).ToOperand());
230  second.source_info().MakeStatementPosition(0);
231  optimizer()->Write(&first);
232  CHECK_EQ(write_count(), 0);
233  optimizer()->Write(&second);
234  CHECK_EQ(write_count(), 1);
235  CHECK_EQ(last_written(), first);
236  optimizer()->Write(&third);
237  CHECK_EQ(write_count(), 1);
238  Flush();
239  CHECK_EQ(write_count(), 2);
240  CHECK_EQ(last_written(), third);
241}
242
243TEST_F(BytecodePeepholeOptimizerTest, LdarToName) {
244  BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand());
245  BytecodeNode second(Bytecode::kToName);
246  optimizer()->Write(&first);
247  CHECK_EQ(write_count(), 0);
248  optimizer()->Write(&second);
249  CHECK_EQ(write_count(), 1);
250  CHECK_EQ(last_written(), first);
251  Flush();
252  CHECK_EQ(write_count(), 2);
253  CHECK_EQ(last_written(), second);
254}
255
256TEST_F(BytecodePeepholeOptimizerTest, ToNameToName) {
257  BytecodeNode first(Bytecode::kToName);
258  BytecodeNode second(Bytecode::kToName);
259  optimizer()->Write(&first);
260  CHECK_EQ(write_count(), 0);
261  optimizer()->Write(&second);
262  CHECK_EQ(write_count(), 1);
263  CHECK_EQ(last_written(), first);
264  Flush();
265  CHECK_EQ(write_count(), 1);
266}
267
268TEST_F(BytecodePeepholeOptimizerTest, TypeOfToName) {
269  BytecodeNode first(Bytecode::kTypeOf);
270  BytecodeNode second(Bytecode::kToName);
271  optimizer()->Write(&first);
272  CHECK_EQ(write_count(), 0);
273  optimizer()->Write(&second);
274  CHECK_EQ(write_count(), 1);
275  CHECK_EQ(last_written(), first);
276  Flush();
277  CHECK_EQ(write_count(), 1);
278}
279
280TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) {
281  Handle<Object> word =
282      isolate()->factory()->NewStringFromStaticChars("optimizing");
283  size_t index = constant_array()->Insert(word);
284  BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index));
285  BytecodeNode second(Bytecode::kToName);
286  optimizer()->Write(&first);
287  CHECK_EQ(write_count(), 0);
288  optimizer()->Write(&second);
289  CHECK_EQ(write_count(), 1);
290  CHECK_EQ(last_written(), first);
291  Flush();
292  CHECK_EQ(write_count(), 1);
293}
294
295TEST_F(BytecodePeepholeOptimizerTest, LdaConstantNumberToName) {
296  Handle<Object> word = isolate()->factory()->NewNumber(0.380);
297  size_t index = constant_array()->Insert(word);
298  BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index));
299  BytecodeNode second(Bytecode::kToName);
300  optimizer()->Write(&first);
301  CHECK_EQ(write_count(), 0);
302  optimizer()->Write(&second);
303  CHECK_EQ(write_count(), 1);
304  CHECK_EQ(last_written(), first);
305  Flush();
306  CHECK_EQ(write_count(), 2);
307  CHECK_EQ(last_written(), second);
308}
309
310// Tests covering BytecodePeepholeOptimizer::CanElideLast().
311
312TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalse) {
313  BytecodeNode first(Bytecode::kLdaTrue);
314  BytecodeNode second(Bytecode::kLdaFalse);
315  optimizer()->Write(&first);
316  CHECK_EQ(write_count(), 0);
317  optimizer()->Write(&second);
318  CHECK_EQ(write_count(), 0);
319  Flush();
320  CHECK_EQ(write_count(), 1);
321  CHECK_EQ(last_written(), second);
322}
323
324TEST_F(BytecodePeepholeOptimizerTest, LdaTrueStatementLdaFalse) {
325  BytecodeNode first(Bytecode::kLdaTrue);
326  first.source_info().MakeExpressionPosition(3);
327  BytecodeNode second(Bytecode::kLdaFalse);
328  optimizer()->Write(&first);
329  CHECK_EQ(write_count(), 0);
330  optimizer()->Write(&second);
331  CHECK_EQ(write_count(), 0);
332  Flush();
333  CHECK_EQ(write_count(), 1);
334  CHECK_EQ(last_written(), second);
335  CHECK(second.source_info().is_expression());
336  CHECK_EQ(second.source_info().source_position(), 3);
337}
338
339TEST_F(BytecodePeepholeOptimizerTest, NopStackCheck) {
340  BytecodeNode first(Bytecode::kNop);
341  BytecodeNode second(Bytecode::kStackCheck);
342  optimizer()->Write(&first);
343  CHECK_EQ(write_count(), 0);
344  optimizer()->Write(&second);
345  CHECK_EQ(write_count(), 0);
346  Flush();
347  CHECK_EQ(write_count(), 1);
348  CHECK_EQ(last_written(), second);
349}
350
351TEST_F(BytecodePeepholeOptimizerTest, NopStatementStackCheck) {
352  BytecodeNode first(Bytecode::kNop);
353  first.source_info().MakeExpressionPosition(3);
354  BytecodeNode second(Bytecode::kStackCheck);
355  optimizer()->Write(&first);
356  CHECK_EQ(write_count(), 0);
357  optimizer()->Write(&second);
358  CHECK_EQ(write_count(), 0);
359  Flush();
360  CHECK_EQ(write_count(), 1);
361  second.source_info().MakeExpressionPosition(
362      first.source_info().source_position());
363  CHECK_EQ(last_written(), second);
364}
365
366// Tests covering BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes().
367
368TEST_F(BytecodePeepholeOptimizerTest, MergeLoadICStar) {
369  const uint32_t operands[] = {
370      static_cast<uint32_t>(Register(31).ToOperand()), 32, 33,
371      static_cast<uint32_t>(Register(256).ToOperand())};
372  const int expected_operand_count = static_cast<int>(arraysize(operands));
373
374  BytecodeNode first(Bytecode::kLdaNamedProperty, operands[0], operands[1],
375                     operands[2]);
376  BytecodeNode second(Bytecode::kStar, operands[3]);
377  BytecodeNode third(Bytecode::kReturn);
378  optimizer()->Write(&first);
379  optimizer()->Write(&second);
380  CHECK_EQ(write_count(), 1);
381  CHECK_EQ(last_written().bytecode(), Bytecode::kLdrNamedProperty);
382  CHECK_EQ(last_written().operand_count(), expected_operand_count);
383  for (int i = 0; i < expected_operand_count; ++i) {
384    CHECK_EQ(last_written().operand(i), operands[i]);
385  }
386  optimizer()->Write(&third);
387  CHECK_EQ(write_count(), 2);
388  CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
389  CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
390  Flush();
391  CHECK_EQ(last_written().bytecode(), third.bytecode());
392}
393
394TEST_F(BytecodePeepholeOptimizerTest, MergeLdaKeyedPropertyStar) {
395  const uint32_t operands[] = {static_cast<uint32_t>(Register(31).ToOperand()),
396                               9999997,
397                               static_cast<uint32_t>(Register(1).ToOperand())};
398  const int expected_operand_count = static_cast<int>(arraysize(operands));
399
400  BytecodeNode first(Bytecode::kLdaKeyedProperty, operands[0], operands[1]);
401  BytecodeNode second(Bytecode::kStar, operands[2]);
402  BytecodeNode third(Bytecode::kReturn);
403  optimizer()->Write(&first);
404  optimizer()->Write(&second);
405  CHECK_EQ(write_count(), 1);
406  CHECK_EQ(last_written().bytecode(), Bytecode::kLdrKeyedProperty);
407  CHECK_EQ(last_written().operand_count(), expected_operand_count);
408  for (int i = 0; i < expected_operand_count; ++i) {
409    CHECK_EQ(last_written().operand(i), operands[i]);
410  }
411  optimizer()->Write(&third);
412  CHECK_EQ(write_count(), 2);
413  CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
414  CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
415  Flush();
416  CHECK_EQ(last_written().bytecode(), third.bytecode());
417}
418
419TEST_F(BytecodePeepholeOptimizerTest, MergeLdaGlobalStar) {
420  const uint32_t operands[] = {19191,
421                               static_cast<uint32_t>(Register(1).ToOperand())};
422  const int expected_operand_count = static_cast<int>(arraysize(operands));
423
424  BytecodeNode first(Bytecode::kLdaGlobal, operands[0]);
425  BytecodeNode second(Bytecode::kStar, operands[1]);
426  BytecodeNode third(Bytecode::kReturn);
427  optimizer()->Write(&first);
428  optimizer()->Write(&second);
429  CHECK_EQ(write_count(), 1);
430  CHECK_EQ(last_written().bytecode(), Bytecode::kLdrGlobal);
431  CHECK_EQ(last_written().operand_count(), expected_operand_count);
432  for (int i = 0; i < expected_operand_count; ++i) {
433    CHECK_EQ(last_written().operand(i), operands[i]);
434  }
435  optimizer()->Write(&third);
436  CHECK_EQ(write_count(), 2);
437  CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
438  CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
439  Flush();
440  CHECK_EQ(last_written().bytecode(), third.bytecode());
441}
442
443TEST_F(BytecodePeepholeOptimizerTest, MergeLdaContextSlotStar) {
444  const uint32_t operands[] = {
445      static_cast<uint32_t>(Register(200000).ToOperand()), 55005500,
446      static_cast<uint32_t>(Register(1).ToOperand())};
447  const int expected_operand_count = static_cast<int>(arraysize(operands));
448
449  BytecodeNode first(Bytecode::kLdaContextSlot, operands[0], operands[1]);
450  BytecodeNode second(Bytecode::kStar, operands[2]);
451  BytecodeNode third(Bytecode::kReturn);
452  optimizer()->Write(&first);
453  optimizer()->Write(&second);
454  CHECK_EQ(write_count(), 1);
455  CHECK_EQ(last_written().bytecode(), Bytecode::kLdrContextSlot);
456  CHECK_EQ(last_written().operand_count(), expected_operand_count);
457  for (int i = 0; i < expected_operand_count; ++i) {
458    CHECK_EQ(last_written().operand(i), operands[i]);
459  }
460  optimizer()->Write(&third);
461  CHECK_EQ(write_count(), 2);
462  CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
463  CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
464  Flush();
465  CHECK_EQ(last_written().bytecode(), third.bytecode());
466}
467
468TEST_F(BytecodePeepholeOptimizerTest, MergeLdaUndefinedStar) {
469  const uint32_t operands[] = {
470      static_cast<uint32_t>(Register(100000).ToOperand())};
471  const int expected_operand_count = static_cast<int>(arraysize(operands));
472
473  BytecodeNode first(Bytecode::kLdaUndefined);
474  BytecodeNode second(Bytecode::kStar, operands[0]);
475  BytecodeNode third(Bytecode::kReturn);
476  optimizer()->Write(&first);
477  optimizer()->Write(&second);
478  CHECK_EQ(write_count(), 1);
479  CHECK_EQ(last_written().bytecode(), Bytecode::kLdrUndefined);
480  CHECK_EQ(last_written().operand_count(), expected_operand_count);
481  for (int i = 0; i < expected_operand_count; ++i) {
482    CHECK_EQ(last_written().operand(i), operands[i]);
483  }
484  optimizer()->Write(&third);
485  CHECK_EQ(write_count(), 2);
486  CHECK_EQ(last_written().bytecode(), Bytecode::kLdar);
487  CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]);
488  Flush();
489  CHECK_EQ(last_written().bytecode(), third.bytecode());
490}
491
492}  // namespace interpreter
493}  // namespace internal
494}  // namespace v8
495