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