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