Home | History | Annotate | Download | only in interpreter
      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:
     19   BytecodeArrayBuilderTest() {}
     20   ~BytecodeArrayBuilderTest() override {}
     21 };
     22 
     23 
     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 
    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 
    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 
    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 
    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 
    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 
    515 static Bytecode PeepholeToBoolean(Bytecode jump_bytecode) {
    516   return FLAG_ignition_peephole
    517              ? Bytecodes::GetJumpWithoutToBoolean(jump_bytecode)
    518              : jump_bytecode;
    519 }
    520 
    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 
    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 
    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 
    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