• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace v8 {
14 namespace internal {
15 namespace interpreter {
16 
17 class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
18  public:
BytecodeArrayBuilderTest()19   BytecodeArrayBuilderTest() {}
~BytecodeArrayBuilderTest()20   ~BytecodeArrayBuilderTest() override {}
21 };
22 
23 
TEST_F(BytecodeArrayBuilderTest,AllBytecodesGenerated)24 TEST_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 
TEST_F(BytecodeArrayBuilderTest,FrameSizesLookGood)418 TEST_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 
TEST_F(BytecodeArrayBuilderTest,RegisterValues)450 TEST_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 
TEST_F(BytecodeArrayBuilderTest,Parameters)462 TEST_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 
TEST_F(BytecodeArrayBuilderTest,RegisterType)471 TEST_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 
TEST_F(BytecodeArrayBuilderTest,Constants)494 TEST_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 
PeepholeToBoolean(Bytecode jump_bytecode)515 static Bytecode PeepholeToBoolean(Bytecode jump_bytecode) {
516   return FLAG_ignition_peephole
517              ? Bytecodes::GetJumpWithoutToBoolean(jump_bytecode)
518              : jump_bytecode;
519 }
520 
TEST_F(BytecodeArrayBuilderTest,ForwardJumps)521 TEST_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 
TEST_F(BytecodeArrayBuilderTest,BackwardJumps)642 TEST_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 
TEST_F(BytecodeArrayBuilderTest,LabelReuse)759 TEST_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 
TEST_F(BytecodeArrayBuilderTest,LabelAddressReuse)791 TEST_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