1// Copyright 2014 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-array-builder.h"
8#include "src/interpreter/bytecode-array-iterator.h"
9#include "src/interpreter/bytecode-label.h"
10#include "src/interpreter/bytecode-register-allocator.h"
11#include "test/unittests/test-utils.h"
12
13namespace v8 {
14namespace internal {
15namespace interpreter {
16
17class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
18 public:
19  BytecodeArrayBuilderTest() {}
20  ~BytecodeArrayBuilderTest() override {}
21};
22
23
24TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
25  BytecodeArrayBuilder builder(isolate(), zone(), 0, 1, 131);
26  Factory* factory = isolate()->factory();
27
28  CHECK_EQ(builder.locals_count(), 131);
29  CHECK_EQ(builder.context_count(), 1);
30  CHECK_EQ(builder.fixed_register_count(), 132);
31
32  Register reg(0);
33  Register other(reg.index() + 1);
34  Register wide(128);
35
36  // Emit argument creation operations.
37  builder.CreateArguments(CreateArgumentsType::kMappedArguments)
38      .CreateArguments(CreateArgumentsType::kUnmappedArguments)
39      .CreateArguments(CreateArgumentsType::kRestParameter);
40
41  // Emit constant loads.
42  builder.LoadLiteral(Smi::FromInt(0))
43      .StoreAccumulatorInRegister(reg)
44      .LoadLiteral(Smi::FromInt(8))
45      .StoreAccumulatorInRegister(reg)
46      .LoadLiteral(Smi::FromInt(10000000))
47      .StoreAccumulatorInRegister(reg)
48      .LoadLiteral(factory->NewStringFromStaticChars("A constant"))
49      .StoreAccumulatorInRegister(reg)
50      .LoadUndefined()
51      .Debugger()  // Prevent peephole optimization LdaNull, Star -> LdrNull.
52      .LoadNull()
53      .StoreAccumulatorInRegister(reg)
54      .LoadTheHole()
55      .StoreAccumulatorInRegister(reg)
56      .LoadTrue()
57      .StoreAccumulatorInRegister(reg)
58      .LoadFalse()
59      .StoreAccumulatorInRegister(wide);
60
61  // Emit Ldar and Star taking care to foil the register optimizer.
62  builder.StackCheck(0)
63      .LoadAccumulatorWithRegister(other)
64      .BinaryOperation(Token::ADD, reg)
65      .StoreAccumulatorInRegister(reg)
66      .LoadNull();
67
68  // Emit register-register transfer.
69  builder.MoveRegister(reg, other);
70  builder.MoveRegister(reg, wide);
71
72  // Emit global load / store operations.
73  Handle<String> name = factory->NewStringFromStaticChars("var_name");
74  builder.LoadGlobal(1, TypeofMode::NOT_INSIDE_TYPEOF)
75      .LoadGlobal(1, TypeofMode::INSIDE_TYPEOF)
76      .StoreGlobal(name, 1, LanguageMode::SLOPPY)
77      .StoreGlobal(name, 1, LanguageMode::STRICT);
78
79  // Emit context operations.
80  builder.PushContext(reg)
81      .PopContext(reg)
82      .LoadContextSlot(reg, 1)
83      .StoreContextSlot(reg, 1);
84
85  // Emit load / store property operations.
86  builder.LoadNamedProperty(reg, name, 0)
87      .LoadKeyedProperty(reg, 0)
88      .StoreNamedProperty(reg, name, 0, LanguageMode::SLOPPY)
89      .StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY)
90      .StoreNamedProperty(reg, name, 0, LanguageMode::STRICT)
91      .StoreKeyedProperty(reg, reg, 0, LanguageMode::STRICT);
92
93  // Emit load / store lookup slots.
94  builder.LoadLookupSlot(name, TypeofMode::NOT_INSIDE_TYPEOF)
95      .LoadLookupSlot(name, TypeofMode::INSIDE_TYPEOF)
96      .StoreLookupSlot(name, LanguageMode::SLOPPY)
97      .StoreLookupSlot(name, LanguageMode::STRICT);
98
99  // Emit closure operations.
100  Handle<SharedFunctionInfo> shared_info = factory->NewSharedFunctionInfo(
101      factory->NewStringFromStaticChars("function_a"), MaybeHandle<Code>(),
102      false);
103  builder.CreateClosure(shared_info, NOT_TENURED);
104
105  // Emit literal creation operations.
106  builder.CreateRegExpLiteral(factory->NewStringFromStaticChars("a"), 0, 0)
107      .CreateArrayLiteral(factory->NewFixedArray(1), 0, 0)
108      .CreateObjectLiteral(factory->NewFixedArray(1), 0, 0);
109
110  // Call operations.
111  builder.Call(reg, other, 1, 0)
112      .Call(reg, wide, 1, 0)
113      .TailCall(reg, other, 1, 0)
114      .TailCall(reg, wide, 1, 0)
115      .CallRuntime(Runtime::kIsArray, reg, 1)
116      .CallRuntime(Runtime::kIsArray, wide, 1)
117      .CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg, 1, other)
118      .CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, wide, 1, other)
119      .CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg, 1)
120      .CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, wide, 1);
121
122  // Emit binary operator invocations.
123  builder.BinaryOperation(Token::Value::ADD, reg)
124      .BinaryOperation(Token::Value::SUB, reg)
125      .BinaryOperation(Token::Value::MUL, reg)
126      .BinaryOperation(Token::Value::DIV, reg)
127      .BinaryOperation(Token::Value::MOD, reg);
128
129  // Emit bitwise operator invocations
130  builder.BinaryOperation(Token::Value::BIT_OR, reg)
131      .BinaryOperation(Token::Value::BIT_XOR, reg)
132      .BinaryOperation(Token::Value::BIT_AND, reg);
133
134  // Emit shift operator invocations
135  builder.BinaryOperation(Token::Value::SHL, reg)
136      .BinaryOperation(Token::Value::SAR, reg)
137      .BinaryOperation(Token::Value::SHR, reg);
138
139  // Emit count operatior invocations
140  builder.CountOperation(Token::Value::ADD).CountOperation(Token::Value::SUB);
141
142  // Emit unary operator invocations.
143  builder
144      .LogicalNot()  // ToBooleanLogicalNot
145      .LogicalNot()  // non-ToBoolean LogicalNot
146      .TypeOf();
147
148  // Emit delete
149  builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
150
151  // Emit new.
152  builder.New(reg, reg, 0);
153  builder.New(wide, wide, 0);
154
155  // Emit test operator invocations.
156  builder.CompareOperation(Token::Value::EQ, reg)
157      .CompareOperation(Token::Value::NE, reg)
158      .CompareOperation(Token::Value::EQ_STRICT, reg)
159      .CompareOperation(Token::Value::LT, reg)
160      .CompareOperation(Token::Value::GT, reg)
161      .CompareOperation(Token::Value::LTE, reg)
162      .CompareOperation(Token::Value::GTE, reg)
163      .CompareOperation(Token::Value::INSTANCEOF, reg)
164      .CompareOperation(Token::Value::IN, reg);
165
166  // Emit cast operator invocations.
167  builder.CastAccumulatorToNumber()
168      .CastAccumulatorToJSObject()
169      .CastAccumulatorToName();
170
171  // Emit control flow. Return must be the last instruction.
172  BytecodeLabel start;
173  builder.Bind(&start);
174  {
175    // Short jumps with Imm8 operands
176    BytecodeLabel after_jump;
177    builder.Jump(&start)
178        .Bind(&after_jump)
179        .JumpIfNull(&start)
180        .JumpIfUndefined(&start)
181        .JumpIfNotHole(&start);
182  }
183
184  // Longer jumps with constant operands
185  BytecodeLabel end[8];
186  {
187    BytecodeLabel after_jump;
188    builder.Jump(&end[0])
189        .Bind(&after_jump)
190        .LoadTrue()
191        .JumpIfTrue(&end[1])
192        .LoadTrue()
193        .JumpIfFalse(&end[2])
194        .LoadLiteral(Smi::FromInt(0))
195        .JumpIfTrue(&end[3])
196        .LoadLiteral(Smi::FromInt(0))
197        .JumpIfFalse(&end[4])
198        .JumpIfNull(&end[5])
199        .JumpIfUndefined(&end[6])
200        .JumpIfNotHole(&end[7]);
201  }
202
203  // Perform an operation that returns boolean value to
204  // generate JumpIfTrue/False
205  builder.CompareOperation(Token::Value::EQ, reg)
206      .JumpIfTrue(&start)
207      .CompareOperation(Token::Value::EQ, reg)
208      .JumpIfFalse(&start);
209  // Perform an operation that returns a non-boolean operation to
210  // generate JumpIfToBooleanTrue/False.
211  builder.BinaryOperation(Token::Value::ADD, reg)
212      .JumpIfTrue(&start)
213      .BinaryOperation(Token::Value::ADD, reg)
214      .JumpIfFalse(&start);
215  // Insert dummy ops to force longer jumps
216  for (int i = 0; i < 128; i++) {
217    builder.LoadTrue();
218  }
219  // Longer jumps requiring Constant operand
220  {
221    BytecodeLabel after_jump;
222    builder.Jump(&start)
223        .Bind(&after_jump)
224        .JumpIfNull(&start)
225        .JumpIfUndefined(&start)
226        .JumpIfNotHole(&start);
227    // Perform an operation that returns boolean value to
228    // generate JumpIfTrue/False
229    builder.CompareOperation(Token::Value::EQ, reg)
230        .JumpIfTrue(&start)
231        .CompareOperation(Token::Value::EQ, reg)
232        .JumpIfFalse(&start);
233    // Perform an operation that returns a non-boolean operation to
234    // generate JumpIfToBooleanTrue/False.
235    builder.BinaryOperation(Token::Value::ADD, reg)
236        .JumpIfTrue(&start)
237        .BinaryOperation(Token::Value::ADD, reg)
238        .JumpIfFalse(&start);
239  }
240
241  // Emit stack check bytecode.
242  builder.StackCheck(0);
243
244  // Emit throw and re-throw in it's own basic block so that the rest of the
245  // code isn't omitted due to being dead.
246  BytecodeLabel after_throw;
247  builder.Throw().Bind(&after_throw);
248  BytecodeLabel after_rethrow;
249  builder.ReThrow().Bind(&after_rethrow);
250
251  builder.ForInPrepare(reg)
252      .ForInDone(reg, reg)
253      .ForInNext(reg, reg, reg, 1)
254      .ForInStep(reg);
255  builder.ForInPrepare(wide)
256      .ForInDone(reg, other)
257      .ForInNext(wide, wide, wide, 1024)
258      .ForInStep(reg);
259
260  // Wide constant pool loads
261  for (int i = 0; i < 256; i++) {
262    // Emit junk in constant pool to force wide constant pool index.
263    builder.LoadLiteral(factory->NewNumber(2.5321 + i));
264  }
265  builder.LoadLiteral(Smi::FromInt(20000000));
266  Handle<String> wide_name = factory->NewStringFromStaticChars("var_wide_name");
267
268  // Emit wide global load / store operations.
269  builder.LoadGlobal(1024, TypeofMode::NOT_INSIDE_TYPEOF)
270      .LoadGlobal(1024, TypeofMode::INSIDE_TYPEOF)
271      .LoadGlobal(1024, TypeofMode::INSIDE_TYPEOF)
272      .StoreGlobal(name, 1024, LanguageMode::SLOPPY)
273      .StoreGlobal(wide_name, 1, LanguageMode::STRICT);
274
275  // Emit extra wide global load.
276  builder.LoadGlobal(1024 * 1024, TypeofMode::NOT_INSIDE_TYPEOF);
277
278  // Emit wide load / store property operations.
279  builder.LoadNamedProperty(reg, wide_name, 0)
280      .LoadKeyedProperty(reg, 2056)
281      .StoreNamedProperty(reg, wide_name, 0, LanguageMode::SLOPPY)
282      .StoreKeyedProperty(reg, reg, 2056, LanguageMode::SLOPPY)
283      .StoreNamedProperty(reg, wide_name, 0, LanguageMode::STRICT)
284      .StoreKeyedProperty(reg, reg, 2056, LanguageMode::STRICT);
285
286  // Emit wide context operations.
287  builder.LoadContextSlot(reg, 1024).StoreContextSlot(reg, 1024);
288
289  // Emit wide load / store lookup slots.
290  builder.LoadLookupSlot(wide_name, TypeofMode::NOT_INSIDE_TYPEOF)
291      .LoadLookupSlot(wide_name, TypeofMode::INSIDE_TYPEOF)
292      .StoreLookupSlot(wide_name, LanguageMode::SLOPPY)
293      .StoreLookupSlot(wide_name, LanguageMode::STRICT);
294
295  // Emit loads which will be transformed to Ldr equivalents by the peephole
296  // optimizer.
297  builder.LoadNamedProperty(reg, name, 0)
298      .StoreAccumulatorInRegister(reg)
299      .LoadKeyedProperty(reg, 0)
300      .StoreAccumulatorInRegister(reg)
301      .LoadContextSlot(reg, 1)
302      .StoreAccumulatorInRegister(reg)
303      .LoadGlobal(0, TypeofMode::NOT_INSIDE_TYPEOF)
304      .StoreAccumulatorInRegister(reg)
305      .LoadUndefined()
306      .StoreAccumulatorInRegister(reg);
307
308  // CreateClosureWide
309  Handle<SharedFunctionInfo> shared_info2 = factory->NewSharedFunctionInfo(
310      factory->NewStringFromStaticChars("function_b"), MaybeHandle<Code>(),
311      false);
312  builder.CreateClosure(shared_info2, NOT_TENURED);
313
314  // Emit wide variant of literal creation operations.
315  builder.CreateRegExpLiteral(factory->NewStringFromStaticChars("wide_literal"),
316                              0, 0)
317      .CreateArrayLiteral(factory->NewFixedArray(2), 0, 0)
318      .CreateObjectLiteral(factory->NewFixedArray(2), 0, 0);
319
320  // Longer jumps requiring ConstantWide operand
321  {
322    BytecodeLabel after_jump;
323    builder.Jump(&start)
324        .Bind(&after_jump)
325        .JumpIfNull(&start)
326        .JumpIfUndefined(&start)
327        .JumpIfNotHole(&start);
328  }
329
330  // Perform an operation that returns boolean value to
331  // generate JumpIfTrue/False
332  builder.CompareOperation(Token::Value::EQ, reg)
333      .JumpIfTrue(&start)
334      .CompareOperation(Token::Value::EQ, reg)
335      .JumpIfFalse(&start);
336
337  // Perform an operation that returns a non-boolean operation to
338  // generate JumpIfToBooleanTrue/False.
339  builder.BinaryOperation(Token::Value::ADD, reg)
340      .JumpIfTrue(&start)
341      .BinaryOperation(Token::Value::ADD, reg)
342      .JumpIfFalse(&start);
343
344  // Emit generator operations
345  builder.SuspendGenerator(reg)
346      .ResumeGenerator(reg);
347
348  // Intrinsics handled by the interpreter.
349  builder.CallRuntime(Runtime::kInlineIsArray, reg, 1)
350      .CallRuntime(Runtime::kInlineIsArray, wide, 1);
351
352  builder.Debugger();
353  for (size_t i = 0; i < arraysize(end); i++) {
354    builder.Bind(&end[i]);
355  }
356  builder.Return();
357
358  // Generate BytecodeArray.
359  Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
360  CHECK_EQ(the_array->frame_size(),
361           builder.fixed_and_temporary_register_count() * kPointerSize);
362
363  // Build scorecard of bytecodes encountered in the BytecodeArray.
364  std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
365
366  Bytecode final_bytecode = Bytecode::kLdaZero;
367  int i = 0;
368  while (i < the_array->length()) {
369    uint8_t code = the_array->get(i);
370    scorecard[code] += 1;
371    final_bytecode = Bytecodes::FromByte(code);
372    OperandScale operand_scale = OperandScale::kSingle;
373    int prefix_offset = 0;
374    if (Bytecodes::IsPrefixScalingBytecode(final_bytecode)) {
375      operand_scale = Bytecodes::PrefixBytecodeToOperandScale(final_bytecode);
376      prefix_offset = 1;
377      code = the_array->get(i + 1);
378      final_bytecode = Bytecodes::FromByte(code);
379    }
380    i += prefix_offset + Bytecodes::Size(final_bytecode, operand_scale);
381  }
382
383  // Insert entry for illegal bytecode as this is never willingly emitted.
384  scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1;
385
386  // Insert entry for nop bytecode as this often gets optimized out.
387  scorecard[Bytecodes::ToByte(Bytecode::kNop)] = 1;
388
389  if (!FLAG_ignition_peephole) {
390    // Insert entries for bytecodes only emitted by peephole optimizer.
391    scorecard[Bytecodes::ToByte(Bytecode::kLdrNamedProperty)] = 1;
392    scorecard[Bytecodes::ToByte(Bytecode::kLdrKeyedProperty)] = 1;
393    scorecard[Bytecodes::ToByte(Bytecode::kLdrGlobal)] = 1;
394    scorecard[Bytecodes::ToByte(Bytecode::kLdrContextSlot)] = 1;
395    scorecard[Bytecodes::ToByte(Bytecode::kLdrUndefined)] = 1;
396    scorecard[Bytecodes::ToByte(Bytecode::kLogicalNot)] = 1;
397    scorecard[Bytecodes::ToByte(Bytecode::kJump)] = 1;
398    scorecard[Bytecodes::ToByte(Bytecode::kJumpIfTrue)] = 1;
399    scorecard[Bytecodes::ToByte(Bytecode::kJumpIfFalse)] = 1;
400    scorecard[Bytecodes::ToByte(Bytecode::kJumpIfTrueConstant)] = 1;
401    scorecard[Bytecodes::ToByte(Bytecode::kJumpIfFalseConstant)] = 1;
402  }
403
404  // Check return occurs at the end and only once in the BytecodeArray.
405  CHECK_EQ(final_bytecode, Bytecode::kReturn);
406  CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
407
408#define CHECK_BYTECODE_PRESENT(Name, ...)                                \
409  /* Check Bytecode is marked in scorecard, unless it's a debug break */ \
410  if (!Bytecodes::IsDebugBreak(Bytecode::k##Name)) {                     \
411    CHECK_GE(scorecard[Bytecodes::ToByte(Bytecode::k##Name)], 1);        \
412  }
413  BYTECODE_LIST(CHECK_BYTECODE_PRESENT)
414#undef CHECK_BYTECODE_PRESENT
415}
416
417
418TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
419  for (int locals = 0; locals < 5; locals++) {
420    for (int contexts = 0; contexts < 4; contexts++) {
421      for (int temps = 0; temps < 3; temps++) {
422        BytecodeArrayBuilder builder(isolate(), zone(), 0, contexts, locals);
423        BytecodeRegisterAllocator temporaries(
424            zone(), builder.temporary_register_allocator());
425        for (int i = 0; i < locals + contexts; i++) {
426          builder.LoadLiteral(Smi::FromInt(0));
427          builder.StoreAccumulatorInRegister(Register(i));
428        }
429        for (int i = 0; i < temps; i++) {
430          builder.LoadLiteral(Smi::FromInt(0));
431          builder.StoreAccumulatorInRegister(temporaries.NewRegister());
432        }
433        if (temps > 0) {
434          // Ensure temporaries are used so not optimized away by the
435          // register optimizer.
436          builder.New(Register(locals + contexts), Register(locals + contexts),
437                      static_cast<size_t>(temps));
438        }
439        builder.Return();
440
441        Handle<BytecodeArray> the_array = builder.ToBytecodeArray();
442        int total_registers = locals + contexts + temps;
443        CHECK_EQ(the_array->frame_size(), total_registers * kPointerSize);
444      }
445    }
446  }
447}
448
449
450TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
451  int index = 1;
452
453  Register the_register(index);
454  CHECK_EQ(the_register.index(), index);
455
456  int actual_operand = the_register.ToOperand();
457  int actual_index = Register::FromOperand(actual_operand).index();
458  CHECK_EQ(actual_index, index);
459}
460
461
462TEST_F(BytecodeArrayBuilderTest, Parameters) {
463  BytecodeArrayBuilder builder(isolate(), zone(), 10, 0, 0);
464
465  Register param0(builder.Parameter(0));
466  Register param9(builder.Parameter(9));
467  CHECK_EQ(param9.index() - param0.index(), 9);
468}
469
470
471TEST_F(BytecodeArrayBuilderTest, RegisterType) {
472  BytecodeArrayBuilder builder(isolate(), zone(), 10, 0, 3);
473  BytecodeRegisterAllocator register_allocator(
474      zone(), builder.temporary_register_allocator());
475  Register temp0 = register_allocator.NewRegister();
476  Register param0(builder.Parameter(0));
477  Register param9(builder.Parameter(9));
478  Register temp1 = register_allocator.NewRegister();
479  Register reg0(0);
480  Register reg1(1);
481  Register reg2(2);
482  Register temp2 = register_allocator.NewRegister();
483  CHECK_EQ(builder.RegisterIsParameterOrLocal(temp0), false);
484  CHECK_EQ(builder.RegisterIsParameterOrLocal(temp1), false);
485  CHECK_EQ(builder.RegisterIsParameterOrLocal(temp2), false);
486  CHECK_EQ(builder.RegisterIsParameterOrLocal(param0), true);
487  CHECK_EQ(builder.RegisterIsParameterOrLocal(param9), true);
488  CHECK_EQ(builder.RegisterIsParameterOrLocal(reg0), true);
489  CHECK_EQ(builder.RegisterIsParameterOrLocal(reg1), true);
490  CHECK_EQ(builder.RegisterIsParameterOrLocal(reg2), true);
491}
492
493
494TEST_F(BytecodeArrayBuilderTest, Constants) {
495  BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
496
497  Factory* factory = isolate()->factory();
498  Handle<HeapObject> heap_num_1 = factory->NewHeapNumber(3.14);
499  Handle<HeapObject> heap_num_2 = factory->NewHeapNumber(5.2);
500  Handle<Object> large_smi(Smi::FromInt(0x12345678), isolate());
501  Handle<HeapObject> heap_num_2_copy(*heap_num_2);
502  builder.LoadLiteral(heap_num_1)
503      .LoadLiteral(heap_num_2)
504      .LoadLiteral(large_smi)
505      .LoadLiteral(heap_num_1)
506      .LoadLiteral(heap_num_1)
507      .LoadLiteral(heap_num_2_copy)
508      .Return();
509
510  Handle<BytecodeArray> array = builder.ToBytecodeArray();
511  // Should only have one entry for each identical constant.
512  CHECK_EQ(array->constant_pool()->length(), 3);
513}
514
515static Bytecode PeepholeToBoolean(Bytecode jump_bytecode) {
516  return FLAG_ignition_peephole
517             ? Bytecodes::GetJumpWithoutToBoolean(jump_bytecode)
518             : jump_bytecode;
519}
520
521TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
522  static const int kFarJumpDistance = 256;
523
524  BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 1);
525
526  Register reg(0);
527  BytecodeLabel far0, far1, far2, far3, far4;
528  BytecodeLabel near0, near1, near2, near3, near4;
529  BytecodeLabel after_jump0, after_jump1;
530
531  builder.Jump(&near0)
532      .Bind(&after_jump0)
533      .CompareOperation(Token::Value::EQ, reg)
534      .JumpIfTrue(&near1)
535      .CompareOperation(Token::Value::EQ, reg)
536      .JumpIfFalse(&near2)
537      .BinaryOperation(Token::Value::ADD, reg)
538      .JumpIfTrue(&near3)
539      .BinaryOperation(Token::Value::ADD, reg)
540      .JumpIfFalse(&near4)
541      .Bind(&near0)
542      .Bind(&near1)
543      .Bind(&near2)
544      .Bind(&near3)
545      .Bind(&near4)
546      .Jump(&far0)
547      .Bind(&after_jump1)
548      .CompareOperation(Token::Value::EQ, reg)
549      .JumpIfTrue(&far1)
550      .CompareOperation(Token::Value::EQ, reg)
551      .JumpIfFalse(&far2)
552      .BinaryOperation(Token::Value::ADD, reg)
553      .JumpIfTrue(&far3)
554      .BinaryOperation(Token::Value::ADD, reg)
555      .JumpIfFalse(&far4);
556  for (int i = 0; i < kFarJumpDistance - 18; i++) {
557    builder.Debugger();
558  }
559  builder.Bind(&far0).Bind(&far1).Bind(&far2).Bind(&far3).Bind(&far4);
560  builder.Return();
561
562  Handle<BytecodeArray> array = builder.ToBytecodeArray();
563  DCHECK_EQ(array->length(), 36 + kFarJumpDistance - 18 + 1);
564
565  BytecodeArrayIterator iterator(array);
566  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
567  CHECK_EQ(iterator.GetImmediateOperand(0), 18);
568  iterator.Advance();
569
570  // Ignore compare operation.
571  iterator.Advance();
572
573  CHECK_EQ(iterator.current_bytecode(),
574           PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
575  CHECK_EQ(iterator.GetImmediateOperand(0), 14);
576  iterator.Advance();
577
578  // Ignore compare operation.
579  iterator.Advance();
580
581  CHECK_EQ(iterator.current_bytecode(),
582           PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
583  CHECK_EQ(iterator.GetImmediateOperand(0), 10);
584  iterator.Advance();
585
586  // Ignore add operation.
587  iterator.Advance();
588
589  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
590  CHECK_EQ(iterator.GetImmediateOperand(0), 6);
591  iterator.Advance();
592
593  // Ignore add operation.
594  iterator.Advance();
595
596  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
597  CHECK_EQ(iterator.GetImmediateOperand(0), 2);
598  iterator.Advance();
599
600  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
601  CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
602           Smi::FromInt(kFarJumpDistance));
603  iterator.Advance();
604
605  // Ignore compare operation.
606  iterator.Advance();
607
608  CHECK_EQ(iterator.current_bytecode(),
609           PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrueConstant));
610  CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
611           Smi::FromInt(kFarJumpDistance - 4));
612  iterator.Advance();
613
614  // Ignore compare operation.
615  iterator.Advance();
616
617  CHECK_EQ(iterator.current_bytecode(),
618           PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalseConstant));
619  CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
620           Smi::FromInt(kFarJumpDistance - 8));
621  iterator.Advance();
622
623  // Ignore add operation.
624  iterator.Advance();
625
626  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
627  CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
628           Smi::FromInt(kFarJumpDistance - 12));
629  iterator.Advance();
630
631  // Ignore add operation.
632  iterator.Advance();
633
634  CHECK_EQ(iterator.current_bytecode(),
635           Bytecode::kJumpIfToBooleanFalseConstant);
636  CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
637           Smi::FromInt(kFarJumpDistance - 16));
638  iterator.Advance();
639}
640
641
642TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
643  BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 1);
644
645  Register reg(0);
646
647  BytecodeLabel label0, label1, label2, label3, label4;
648  builder.Bind(&label0)
649      .Jump(&label0)
650      .Bind(&label1)
651      .CompareOperation(Token::Value::EQ, reg)
652      .JumpIfTrue(&label1)
653      .Bind(&label2)
654      .CompareOperation(Token::Value::EQ, reg)
655      .JumpIfFalse(&label2)
656      .Bind(&label3)
657      .BinaryOperation(Token::Value::ADD, reg)
658      .JumpIfTrue(&label3)
659      .Bind(&label4)
660      .BinaryOperation(Token::Value::ADD, reg)
661      .JumpIfFalse(&label4);
662  for (int i = 0; i < 63; i++) {
663    BytecodeLabel after_jump;
664    builder.Jump(&label4).Bind(&after_jump);
665  }
666
667  // Add padding to force wide backwards jumps.
668  for (int i = 0; i < 256; i++) {
669    builder.Debugger();
670  }
671
672  builder.BinaryOperation(Token::Value::ADD, reg).JumpIfFalse(&label4);
673  builder.BinaryOperation(Token::Value::ADD, reg).JumpIfTrue(&label3);
674  builder.CompareOperation(Token::Value::EQ, reg).JumpIfFalse(&label2);
675  builder.CompareOperation(Token::Value::EQ, reg).JumpIfTrue(&label1);
676  builder.Jump(&label0);
677  BytecodeLabel end;
678  builder.Bind(&end);
679  builder.Return();
680
681  Handle<BytecodeArray> array = builder.ToBytecodeArray();
682  BytecodeArrayIterator iterator(array);
683  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
684  CHECK_EQ(iterator.GetImmediateOperand(0), 0);
685  iterator.Advance();
686  // Ignore compare operation.
687  iterator.Advance();
688  CHECK_EQ(iterator.current_bytecode(),
689           PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
690  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
691  CHECK_EQ(iterator.GetImmediateOperand(0), -2);
692  iterator.Advance();
693  // Ignore compare operation.
694  iterator.Advance();
695  CHECK_EQ(iterator.current_bytecode(),
696           PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
697  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
698  CHECK_EQ(iterator.GetImmediateOperand(0), -2);
699  iterator.Advance();
700  // Ignore binary operation.
701  iterator.Advance();
702  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
703  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
704  CHECK_EQ(iterator.GetImmediateOperand(0), -2);
705  iterator.Advance();
706  // Ignore binary operation.
707  iterator.Advance();
708  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
709  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
710  CHECK_EQ(iterator.GetImmediateOperand(0), -2);
711  iterator.Advance();
712  for (int i = 0; i < 63; i++) {
713    CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
714    CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
715    CHECK_EQ(iterator.GetImmediateOperand(0), -i * 2 - 4);
716    iterator.Advance();
717  }
718  // Check padding to force wide backwards jumps.
719  for (int i = 0; i < 256; i++) {
720    CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
721    iterator.Advance();
722  }
723  // Ignore binary operation.
724  iterator.Advance();
725  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
726  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
727  CHECK_EQ(iterator.GetImmediateOperand(0), -389);
728  iterator.Advance();
729  // Ignore binary operation.
730  iterator.Advance();
731  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
732  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
733  CHECK_EQ(iterator.GetImmediateOperand(0), -399);
734  iterator.Advance();
735  // Ignore compare operation.
736  iterator.Advance();
737  CHECK_EQ(iterator.current_bytecode(),
738           PeepholeToBoolean(Bytecode::kJumpIfToBooleanFalse));
739  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
740  CHECK_EQ(iterator.GetImmediateOperand(0), -409);
741  iterator.Advance();
742  // Ignore compare operation.
743  iterator.Advance();
744  CHECK_EQ(iterator.current_bytecode(),
745           PeepholeToBoolean(Bytecode::kJumpIfToBooleanTrue));
746  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
747  CHECK_EQ(iterator.GetImmediateOperand(0), -419);
748  iterator.Advance();
749  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
750  CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
751  CHECK_EQ(iterator.GetImmediateOperand(0), -425);
752  iterator.Advance();
753  CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
754  iterator.Advance();
755  CHECK(iterator.done());
756}
757
758
759TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
760  BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
761
762  // Labels can only have 1 forward reference, but
763  // can be referred to mulitple times once bound.
764  BytecodeLabel label, after_jump0, after_jump1;
765
766  builder.Jump(&label)
767      .Bind(&label)
768      .Jump(&label)
769      .Bind(&after_jump0)
770      .Jump(&label)
771      .Bind(&after_jump1)
772      .Return();
773
774  Handle<BytecodeArray> array = builder.ToBytecodeArray();
775  BytecodeArrayIterator iterator(array);
776  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
777  CHECK_EQ(iterator.GetImmediateOperand(0), 2);
778  iterator.Advance();
779  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
780  CHECK_EQ(iterator.GetImmediateOperand(0), 0);
781  iterator.Advance();
782  CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
783  CHECK_EQ(iterator.GetImmediateOperand(0), -2);
784  iterator.Advance();
785  CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
786  iterator.Advance();
787  CHECK(iterator.done());
788}
789
790
791TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
792  static const int kRepeats = 3;
793
794  BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
795  for (int i = 0; i < kRepeats; i++) {
796    BytecodeLabel label, after_jump0, after_jump1;
797    builder.Jump(&label)
798        .Bind(&label)
799        .Jump(&label)
800        .Bind(&after_jump0)
801        .Jump(&label)
802        .Bind(&after_jump1);
803  }
804  builder.Return();
805
806  Handle<BytecodeArray> array = builder.ToBytecodeArray();
807  BytecodeArrayIterator iterator(array);
808  for (int i = 0; i < kRepeats; i++) {
809    CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
810    CHECK_EQ(iterator.GetImmediateOperand(0), 2);
811    iterator.Advance();
812    CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
813    CHECK_EQ(iterator.GetImmediateOperand(0), 0);
814    iterator.Advance();
815    CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
816    CHECK_EQ(iterator.GetImmediateOperand(0), -2);
817    iterator.Advance();
818  }
819  CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
820  iterator.Advance();
821  CHECK(iterator.done());
822}
823
824}  // namespace interpreter
825}  // namespace internal
826}  // namespace v8
827