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