1 // Copyright 2015 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.
7 #include "src/compiler.h"
8 #include "src/interpreter/bytecode-array-iterator.h"
9 #include "src/interpreter/bytecode-generator.h"
10 #include "src/interpreter/interpreter.h"
11 #include "test/cctest/cctest.h"
15 namespace interpreter {
17 class BytecodeGeneratorHelper {
19 const char* kFunctionName = "f";
21 static const int kLastParamIndex =
22 -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
24 BytecodeGeneratorHelper() {
25 i::FLAG_vector_stores = true;
26 i::FLAG_ignition = true;
27 i::FLAG_ignition_filter = StrDup(kFunctionName);
28 i::FLAG_always_opt = false;
29 CcTest::i_isolate()->interpreter()->Initialize();
33 Factory* factory() { return CcTest::i_isolate()->factory(); }
36 Handle<BytecodeArray> MakeBytecode(const char* script,
37 const char* function_name) {
39 Local<Function> function =
40 Local<Function>::Cast(CcTest::global()->Get(v8_str(function_name)));
41 i::Handle<i::JSFunction> js_function = v8::Utils::OpenHandle(*function);
42 return handle(js_function->shared()->bytecode_array(), CcTest::i_isolate());
46 Handle<BytecodeArray> MakeBytecodeForFunctionBody(const char* body) {
47 ScopedVector<char> program(1024);
48 SNPrintF(program, "function %s() { %s }\n%s();", kFunctionName, body,
50 return MakeBytecode(program.start(), kFunctionName);
53 Handle<BytecodeArray> MakeBytecodeForFunction(const char* function) {
54 ScopedVector<char> program(1024);
55 SNPrintF(program, "%s\n%s();", function, kFunctionName);
56 return MakeBytecode(program.start(), kFunctionName);
61 // Helper macros for handcrafting bytecode sequences.
62 #define B(x) static_cast<uint8_t>(Bytecode::k##x)
63 #define U8(x) static_cast<uint8_t>((x) & 0xff)
64 #define R(x) static_cast<uint8_t>(-(x) & 0xff)
65 #define _ static_cast<uint8_t>(0x5a)
68 // Structure for containing expected bytecode snippets.
70 struct ExpectedSnippet {
71 const char* code_snippet;
75 const uint8_t bytecode[512];
81 static void CheckConstant(int expected, Object* actual) {
82 CHECK_EQ(expected, Smi::cast(actual)->value());
86 static void CheckConstant(double expected, Object* actual) {
87 CHECK_EQ(expected, HeapNumber::cast(actual)->value());
91 static void CheckConstant(const char* expected, Object* actual) {
92 Handle<String> expected_string =
93 CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(expected);
94 CHECK(String::cast(actual)->Equals(*expected_string));
98 static void CheckConstant(Handle<Object> expected, Object* actual) {
99 CHECK(actual == *expected || expected->StrictEquals(actual));
103 template <typename T>
104 static void CheckBytecodeArrayEqual(struct ExpectedSnippet<T> expected,
105 Handle<BytecodeArray> actual,
106 bool has_unknown = false) {
107 CHECK_EQ(actual->frame_size(), expected.frame_size);
108 CHECK_EQ(actual->parameter_count(), expected.parameter_count);
109 CHECK_EQ(actual->length(), expected.bytecode_length);
110 if (expected.constant_count == 0) {
111 CHECK_EQ(actual->constant_pool(), CcTest::heap()->empty_fixed_array());
113 CHECK_EQ(actual->constant_pool()->length(), expected.constant_count);
114 for (int i = 0; i < expected.constant_count; i++) {
115 CheckConstant(expected.constants[i], actual->constant_pool()->get(i));
119 BytecodeArrayIterator iterator(actual);
121 while (!iterator.done()) {
122 int bytecode_index = i++;
123 Bytecode bytecode = iterator.current_bytecode();
124 if (Bytecodes::ToByte(bytecode) != expected.bytecode[bytecode_index]) {
125 std::ostringstream stream;
126 stream << "Check failed: expected bytecode [" << bytecode_index
127 << "] to be " << Bytecodes::ToString(static_cast<Bytecode>(
128 expected.bytecode[bytecode_index]))
129 << " but got " << Bytecodes::ToString(bytecode);
130 FATAL(stream.str().c_str());
132 for (int j = 0; j < Bytecodes::NumberOfOperands(bytecode); ++j, ++i) {
133 uint8_t raw_operand =
134 iterator.GetRawOperand(j, Bytecodes::GetOperandType(bytecode, j));
136 // Check actual bytecode array doesn't have the same byte as the
137 // one we use to specify an unknown byte.
138 CHECK_NE(raw_operand, _);
139 if (expected.bytecode[i] == _) {
143 if (raw_operand != expected.bytecode[i]) {
144 std::ostringstream stream;
145 stream << "Check failed: expected operand [" << j << "] for bytecode ["
146 << bytecode_index << "] to be "
147 << static_cast<unsigned int>(expected.bytecode[i]) << " but got "
148 << static_cast<unsigned int>(raw_operand);
149 FATAL(stream.str().c_str());
157 TEST(PrimitiveReturnStatements) {
158 InitializedHandleScope handle_scope;
159 BytecodeGeneratorHelper helper;
161 ExpectedSnippet<int> snippets[] = {
162 {"", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0},
163 {"return;", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0},
164 {"return null;", 0, 1, 2, {B(LdaNull), B(Return)}, 0},
165 {"return true;", 0, 1, 2, {B(LdaTrue), B(Return)}, 0},
166 {"return false;", 0, 1, 2, {B(LdaFalse), B(Return)}, 0},
167 {"return 0;", 0, 1, 2, {B(LdaZero), B(Return)}, 0},
168 {"return +1;", 0, 1, 3, {B(LdaSmi8), U8(1), B(Return)}, 0},
169 {"return -1;", 0, 1, 3, {B(LdaSmi8), U8(-1), B(Return)}, 0},
170 {"return +127;", 0, 1, 3, {B(LdaSmi8), U8(127), B(Return)}, 0},
171 {"return -128;", 0, 1, 3, {B(LdaSmi8), U8(-128), B(Return)}, 0},
174 for (size_t i = 0; i < arraysize(snippets); i++) {
175 Handle<BytecodeArray> bytecode_array =
176 helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
177 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
182 TEST(PrimitiveExpressions) {
183 InitializedHandleScope handle_scope;
184 BytecodeGeneratorHelper helper;
186 ExpectedSnippet<int> snippets[] = {
187 {"var x = 0; return x;",
199 {"var x = 0; return x + 3;",
206 B(Ldar), R(0), // Easy to spot r1 not really needed here.
207 B(Star), R(1), // Dead store.
208 B(LdaSmi8), U8(3), //
215 for (size_t i = 0; i < arraysize(snippets); i++) {
216 Handle<BytecodeArray> bytecode_array =
217 helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
218 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
224 InitializedHandleScope handle_scope;
225 BytecodeGeneratorHelper helper;
227 ExpectedSnippet<int> snippets[] = {
228 {"function f() { return this; }",
229 0, 1, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0},
230 {"function f(arg1) { return arg1; }",
231 0, 2, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0},
232 {"function f(arg1) { return this; }",
233 0, 2, 3, {B(Ldar), R(helper.kLastParamIndex - 1), B(Return)}, 0},
234 {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return arg4; }",
235 0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 3), B(Return)}, 0},
236 {"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return this; }",
237 0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 7), B(Return)}, 0}
240 for (size_t i = 0; i < arraysize(snippets); i++) {
241 Handle<BytecodeArray> bytecode_array =
242 helper.MakeBytecodeForFunction(snippets[i].code_snippet);
243 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
248 TEST(IntegerConstants) {
249 InitializedHandleScope handle_scope;
250 BytecodeGeneratorHelper helper;
252 ExpectedSnippet<int> snippets[] = {
258 B(LdaConstant), U8(0), //
263 {"var a = 1234; return 5678;",
268 B(LdaConstant), U8(0), //
270 B(LdaConstant), U8(1), //
275 {"var a = 1234; return 1234;",
280 B(LdaConstant), U8(0), //
282 B(LdaConstant), U8(0), //
288 for (size_t i = 0; i < arraysize(snippets); i++) {
289 Handle<BytecodeArray> bytecode_array =
290 helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
291 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
296 TEST(HeapNumberConstants) {
297 InitializedHandleScope handle_scope;
298 BytecodeGeneratorHelper helper;
300 ExpectedSnippet<double> snippets[] = {
306 B(LdaConstant), U8(0), //
311 {"var a = 1.2; return 2.6;",
316 B(LdaConstant), U8(0), //
318 B(LdaConstant), U8(1), //
323 {"var a = 3.14; return 3.14;",
328 B(LdaConstant), U8(0), //
330 B(LdaConstant), U8(1), //
335 for (size_t i = 0; i < arraysize(snippets); i++) {
336 Handle<BytecodeArray> bytecode_array =
337 helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
338 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
343 TEST(StringConstants) {
344 InitializedHandleScope handle_scope;
345 BytecodeGeneratorHelper helper;
347 ExpectedSnippet<const char*> snippets[] = {
348 {"return \"This is a string\";",
353 B(LdaConstant), U8(0), //
357 {"This is a string"}},
358 {"var a = \"First string\"; return \"Second string\";",
363 B(LdaConstant), U8(0), //
365 B(LdaConstant), U8(1), //
369 {"First string", "Second string"}},
370 {"var a = \"Same string\"; return \"Same string\";",
375 B(LdaConstant), U8(0), //
377 B(LdaConstant), U8(0), //
383 for (size_t i = 0; i < arraysize(snippets); i++) {
384 Handle<BytecodeArray> bytecode_array =
385 helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
386 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
391 TEST(PropertyLoads) {
392 InitializedHandleScope handle_scope;
393 BytecodeGeneratorHelper helper;
395 FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC,
396 i::FeedbackVectorSlotKind::LOAD_IC};
397 StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
398 Handle<i::TypeFeedbackVector> vector =
399 helper.factory()->NewTypeFeedbackVector(&feedback_spec);
401 ExpectedSnippet<const char*> snippets[] = {
402 {"function f(a) { return a.name; }\nf({name : \"test\"})",
407 B(Ldar), R(helper.kLastParamIndex), //
409 B(LdaConstant), U8(0), //
410 B(LoadIC), R(0), U8(vector->first_ic_slot_index()), //
415 {"function f(a) { return a[\"key\"]; }\nf({key : \"test\"})",
420 B(Ldar), R(helper.kLastParamIndex), //
422 B(LdaConstant), U8(0), //
423 B(LoadIC), R(0), U8(vector->first_ic_slot_index()), //
428 {"function f(a) { return a[100]; }\nf({100 : \"test\"})",
433 B(Ldar), R(helper.kLastParamIndex), //
435 B(LdaSmi8), U8(100), //
436 B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()), //
440 {"function f(a, b) { return a[b]; }\nf({arg : \"test\"}, \"arg\")",
445 B(Ldar), R(helper.kLastParamIndex - 1), //
447 B(Ldar), R(helper.kLastParamIndex), //
448 B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()), //
452 {"function f(a) { var b = a.name; return a[-124]; }\n"
453 "f({\"-124\" : \"test\", name : 123 })",
458 B(Ldar), R(helper.kLastParamIndex), //
460 B(LdaConstant), U8(0), //
461 B(LoadIC), R(1), U8(vector->first_ic_slot_index()), //
463 B(Ldar), R(helper.kLastParamIndex), //
465 B(LdaSmi8), U8(-124), //
466 B(KeyedLoadIC), R(1), U8(vector->first_ic_slot_index() + 2), //
471 for (size_t i = 0; i < arraysize(snippets); i++) {
472 Handle<BytecodeArray> bytecode_array =
473 helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
474 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
479 TEST(PropertyStores) {
480 InitializedHandleScope handle_scope;
481 BytecodeGeneratorHelper helper;
483 FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::STORE_IC,
484 i::FeedbackVectorSlotKind::STORE_IC};
485 StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
486 Handle<i::TypeFeedbackVector> vector =
487 helper.factory()->NewTypeFeedbackVector(&feedback_spec);
489 ExpectedSnippet<const char*> snippets[] = {
490 {"function f(a) { a.name = \"val\"; }\nf({name : \"test\"})",
495 B(Ldar), R(helper.kLastParamIndex), //
497 B(LdaConstant), U8(0), //
499 B(LdaConstant), U8(1), //
500 B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), //
506 {"function f(a) { a[\"key\"] = \"val\"; }\nf({key : \"test\"})",
511 B(Ldar), R(helper.kLastParamIndex), //
513 B(LdaConstant), U8(0), //
515 B(LdaConstant), U8(1), //
516 B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), //
522 {"function f(a) { a[100] = \"val\"; }\nf({100 : \"test\"})",
527 B(Ldar), R(helper.kLastParamIndex), //
529 B(LdaSmi8), U8(100), //
531 B(LdaConstant), U8(0), //
532 B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), //
538 {"function f(a, b) { a[b] = \"val\"; }\nf({arg : \"test\"}, \"arg\")",
543 B(Ldar), R(helper.kLastParamIndex - 1), //
545 B(Ldar), R(helper.kLastParamIndex), //
547 B(LdaConstant), U8(0), //
548 B(KeyedStoreIC), R(0), R(1), U8(vector->first_ic_slot_index()), //
554 {"function f(a) { a.name = a[-124]; }\n"
555 "f({\"-124\" : \"test\", name : 123 })",
560 B(Ldar), R(helper.kLastParamIndex), //
562 B(LdaConstant), U8(0), //
564 B(Ldar), R(helper.kLastParamIndex), //
566 B(LdaSmi8), U8(-124), //
567 B(KeyedLoadIC), R(2), U8(vector->first_ic_slot_index()), //
568 B(StoreIC), R(0), R(1), U8(vector->first_ic_slot_index() + 2), //
574 for (size_t i = 0; i < arraysize(snippets); i++) {
575 Handle<BytecodeArray> bytecode_array =
576 helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
577 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
582 #define FUNC_ARG "new (function Obj() { this.func = function() { return; }})()"
586 InitializedHandleScope handle_scope;
587 BytecodeGeneratorHelper helper; //
589 FeedbackVectorSlotKind ic_kinds[] = {i::FeedbackVectorSlotKind::LOAD_IC,
590 i::FeedbackVectorSlotKind::LOAD_IC};
591 StaticFeedbackVectorSpec feedback_spec(0, 2, ic_kinds);
592 Handle<i::TypeFeedbackVector> vector =
593 helper.factory()->NewTypeFeedbackVector(&feedback_spec);
595 ExpectedSnippet<const char*> snippets[] = {
596 {"function f(a) { return a.func(); }\nf(" FUNC_ARG ")",
601 B(Ldar), R(helper.kLastParamIndex), //
603 B(LdaConstant), U8(0), //
604 B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), //
606 B(Call), R(0), R(1), U8(0), //
611 {"function f(a, b, c) { return a.func(b, c); }\nf(" FUNC_ARG ", 1, 2)",
616 B(Ldar), R(helper.kLastParamIndex - 2), //
618 B(LdaConstant), U8(0), //
619 B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), //
621 B(Ldar), R(helper.kLastParamIndex - 1), //
623 B(Ldar), R(helper.kLastParamIndex), //
625 B(Call), R(0), R(1), U8(2), //
630 {"function f(a, b) { return a.func(b + b, b); }\nf(" FUNC_ARG ", 1)",
635 B(Ldar), R(helper.kLastParamIndex - 1), //
637 B(LdaConstant), U8(0), //
638 B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), //
640 B(Ldar), R(helper.kLastParamIndex), //
642 B(Ldar), R(helper.kLastParamIndex), //
645 B(Ldar), R(helper.kLastParamIndex), //
647 B(Call), R(0), R(1), U8(2), //
652 for (size_t i = 0; i < arraysize(snippets); i++) {
653 Handle<BytecodeArray> bytecode_array =
654 helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
655 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
661 InitializedHandleScope handle_scope;
662 BytecodeGeneratorHelper helper;
664 ExpectedSnippet<const char*> snippets[] = {
665 {"var a = 1;\nfunction f() { return a; }\nf()",
672 {"function t() { }\nfunction f() { return t; }\nf()",
681 for (size_t i = 0; i < arraysize(snippets); i++) {
682 Handle<BytecodeArray> bytecode_array =
683 helper.MakeBytecode(snippets[i].code_snippet, "f");
684 CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
690 InitializedHandleScope handle_scope;
691 BytecodeGeneratorHelper helper;
693 ExpectedSnippet<const char*> snippets[] = {
694 {"function t() { }\nfunction f() { return t(); }\nf()",
695 2 * kPointerSize, 1, 12,
701 B(Call), R(0), R(1), U8(0),
705 {"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()",
706 5 * kPointerSize, 1, 24,
718 B(Call), R(0), R(1), U8(3),
724 for (size_t i = 0; i < arraysize(snippets); i++) {
725 Handle<BytecodeArray> bytecode_array =
726 helper.MakeBytecode(snippets[i].code_snippet, "f");
727 CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
733 InitializedHandleScope handle_scope;
734 BytecodeGeneratorHelper helper;
736 Handle<Object> unused = helper.factory()->undefined_value();
738 ExpectedSnippet<Handle<Object>> snippets[] = {
739 {"function f() { if (0) { return 1; } else { return -1; } } f()",
745 B(JumpIfFalse), U8(7), //
746 B(LdaSmi8), U8(1), //
748 B(Jump), U8(5), // TODO(oth): Unreachable jump after return
749 B(LdaSmi8), U8(-1), //
754 {unused, unused, unused, unused}},
755 {"function f() { if ('lucky') { return 1; } else { return -1; } } f();",
759 {B(LdaConstant), U8(0), //
761 B(JumpIfFalse), U8(7), //
762 B(LdaSmi8), U8(1), //
764 B(Jump), U8(5), // TODO(oth): Unreachable jump after return
765 B(LdaSmi8), U8(-1), //
770 {helper.factory()->NewStringFromStaticChars("lucky"), unused, unused,
772 {"function f() { if (false) { return 1; } else { return -1; } } f();",
777 B(JumpIfFalse), U8(7), //
778 B(LdaSmi8), U8(1), //
780 B(Jump), U8(5), // TODO(oth): Unreachable jump after return
781 B(LdaSmi8), U8(-1), //
786 {unused, unused, unused, unused}},
787 {"function f(a) { if (a <= 0) { return 200; } else { return -200; } }"
795 B(TestLessThanOrEqual), R(0), //
796 B(JumpIfFalse), U8(7), //
797 B(LdaConstant), U8(0), //
799 B(Jump), U8(5), // TODO(oth): Unreachable jump after return
800 B(LdaConstant), U8(1), //
805 {helper.factory()->NewNumberFromInt(200),
806 helper.factory()->NewNumberFromInt(-200), unused, unused}},
807 {"function f(a, b) { if (a in b) { return 200; } }"
808 "f('prop', { prop: 'yes'});",
816 B(JumpIfFalse), U8(7), //
817 B(LdaConstant), U8(0), //
819 B(Jump), U8(2), // TODO(oth): Unreachable jump after return
823 {helper.factory()->NewNumberFromInt(200), unused, unused, unused}},
824 {"function f(z) { var a = 0; var b = 0; if (a === 0.01) { "
825 #define X "b = a; a = b; "
826 X X X X X X X X X X X X X X X X X X X X X X X X
828 " return 200; } else { return -200; } } f(0.001)",
838 B(LdaConstant), U8(0), //
839 B(TestEqualStrict), R(2), //
840 B(JumpIfFalseConstant), U8(2), //
841 #define X B(Ldar), R(0), B(Star), R(1), B(Ldar), R(1), B(Star), R(0),
842 X X X X X X X X X X X X X X X X X X X X X X X X
847 B(Jump), U8(5), // TODO(oth): Unreachable jump after return
848 B(LdaConstant), U8(3), //
853 {helper.factory()->NewHeapNumber(0.01),
854 helper.factory()->NewNumberFromInt(200),
855 helper.factory()->NewNumberFromInt(199),
856 helper.factory()->NewNumberFromInt(-200)}},
857 {"function f(a, b) {\n"
858 " if (a == b) { return 1; }\n"
859 " if (a === b) { return 1; }\n"
860 " if (a < b) { return 1; }\n"
861 " if (a > b) { return 1; }\n"
862 " if (a <= b) { return 1; }\n"
863 " if (a >= b) { return 1; }\n"
864 " if (a in b) { return 1; }\n"
865 " if (a instanceof b) { return 1; }\n"
866 " /* if (a != b) { return 1; } */" // TODO(oth) Ast visitor yields
867 " /* if (a !== b) { return 1; } */" // UNARY NOT, rather than !=/!==.
874 #define IF_CONDITION_RETURN(condition) \
878 B(condition), R(0), \
879 B(JumpIfFalse), U8(7), \
883 IF_CONDITION_RETURN(TestEqual) //
884 IF_CONDITION_RETURN(TestEqualStrict) //
885 IF_CONDITION_RETURN(TestLessThan) //
886 IF_CONDITION_RETURN(TestGreaterThan) //
887 IF_CONDITION_RETURN(TestLessThanOrEqual) //
888 IF_CONDITION_RETURN(TestGreaterThanOrEqual) //
889 IF_CONDITION_RETURN(TestIn) //
890 IF_CONDITION_RETURN(TestInstanceOf) //
891 #undef IF_CONDITION_RETURN
895 {unused, unused, unused, unused}},
898 for (size_t i = 0; i < arraysize(snippets); i++) {
899 Handle<BytecodeArray> bytecode_array =
900 helper.MakeBytecode(snippets[i].code_snippet, helper.kFunctionName);
901 CheckBytecodeArrayEqual(snippets[i], bytecode_array);
906 } // namespace interpreter
907 } // namespace internal