diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index 10f44a677a..2d0ffa19a3 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -249,6 +249,12 @@ void BytecodeGraphBuilder::VisitStar( } +void BytecodeGraphBuilder::VisitLdaGlobal( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitLoadIC( const interpreter::BytecodeArrayIterator& iterator) { UNIMPLEMENTED(); diff --git a/src/compiler/interpreter-assembler.cc b/src/compiler/interpreter-assembler.cc index 11f75edf8f..8d8c026911 100644 --- a/src/compiler/interpreter-assembler.cc +++ b/src/compiler/interpreter-assembler.cc @@ -315,6 +315,13 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor, } +Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id, + Node* arg1) { + return raw_assembler_->CallRuntime1(function_id, arg1, + ContextTaggedPointer()); +} + + Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2) { return raw_assembler_->CallRuntime2(function_id, arg1, arg2, diff --git a/src/compiler/interpreter-assembler.h b/src/compiler/interpreter-assembler.h index adb7ed8f71..99cf807a25 100644 --- a/src/compiler/interpreter-assembler.h +++ b/src/compiler/interpreter-assembler.h @@ -104,6 +104,7 @@ class InterpreterAssembler { Node* arg2, Node* arg3, Node* arg4, Node* arg5); // Call runtime function. + Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1); Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2); // Returns from the function. diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index f3a1b1ea08..2d06765cd1 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -152,6 +152,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( } +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) { + DCHECK(slot_index >= 0); + if (FitsInByteOperand(slot_index)) { + Output(Bytecode::kLdaGlobal, static_cast(slot_index)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( Register object, int feedback_slot, LanguageMode language_mode) { if (!is_sloppy(language_mode)) { diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index f7a8dddebd..d1277a4ff0 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -49,6 +49,9 @@ class BytecodeArrayBuilder { BytecodeArrayBuilder& LoadTrue(); BytecodeArrayBuilder& LoadFalse(); + // Global loads to accumulator. + BytecodeArrayBuilder& LoadGlobal(int slot_index); + // Register-accumulator transfers. BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg); BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg); diff --git a/src/interpreter/bytecode-array-iterator.cc b/src/interpreter/bytecode-array-iterator.cc index 84be6cc676..dc49308fbe 100644 --- a/src/interpreter/bytecode-array-iterator.cc +++ b/src/interpreter/bytecode-array-iterator.cc @@ -32,8 +32,8 @@ Bytecode BytecodeArrayIterator::current_bytecode() const { } -uint8_t BytecodeArrayIterator::GetOperand(int operand_index, - OperandType operand_type) const { +uint8_t BytecodeArrayIterator::GetRawOperand(int operand_index, + OperandType operand_type) const { DCHECK_GE(operand_index, 0); DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode())); DCHECK_EQ(operand_type, @@ -44,19 +44,19 @@ uint8_t BytecodeArrayIterator::GetOperand(int operand_index, int8_t BytecodeArrayIterator::GetSmi8Operand(int operand_index) const { - uint8_t operand = GetOperand(operand_index, OperandType::kImm8); + uint8_t operand = GetRawOperand(operand_index, OperandType::kImm8); return static_cast(operand); } int BytecodeArrayIterator::GetIndexOperand(int operand_index) const { - uint8_t operand = GetOperand(operand_index, OperandType::kIdx); + uint8_t operand = GetRawOperand(operand_index, OperandType::kIdx); return static_cast(operand); } Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const { - uint8_t operand = GetOperand(operand_index, OperandType::kReg); + uint8_t operand = GetRawOperand(operand_index, OperandType::kReg); return Register::FromOperand(operand); } diff --git a/src/interpreter/bytecode-array-iterator.h b/src/interpreter/bytecode-array-iterator.h index a0de3f0e77..25d99e1cc9 100644 --- a/src/interpreter/bytecode-array-iterator.h +++ b/src/interpreter/bytecode-array-iterator.h @@ -29,9 +29,11 @@ class BytecodeArrayIterator { Register GetRegisterOperand(int operand_index) const; Handle GetConstantForIndexOperand(int operand_index) const; - private: - uint8_t GetOperand(int operand_index, OperandType operand_type) const; + // Get the raw byte for the given operand. Note: you should prefer using the + // typed versions above which cast the return to an appropriate type. + uint8_t GetRawOperand(int operand_index, OperandType operand_type) const; + private: Handle bytecode_array_; int bytecode_offset_; diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index e40a5b3513..55fd7c9945 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -251,7 +251,11 @@ void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) { - Variable* variable = proxy->var(); + VisitVariableLoad(proxy->var()); +} + + +void BytecodeGenerator::VisitVariableLoad(Variable* variable) { switch (variable->location()) { case VariableLocation::LOCAL: { Register source(variable->index()); @@ -265,7 +269,15 @@ void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) { builder().LoadAccumulatorWithRegister(source); break; } - case VariableLocation::GLOBAL: + case VariableLocation::GLOBAL: { + // Global var, const, or let variable. + // TODO(rmcilroy): If context chain depth is short enough, do this using + // a generic version of LoadGlobalViaContextStub rather than calling the + // runtime. + DCHECK(variable->IsStaticGlobalObjectProperty()); + builder().LoadGlobal(variable->index()); + break; + } case VariableLocation::UNALLOCATED: case VariableLocation::CONTEXT: case VariableLocation::LOOKUP: @@ -403,7 +415,15 @@ void BytecodeGenerator::VisitCall(Call* expr) { builder().StoreAccumulatorInRegister(callee); break; } - case Call::GLOBAL_CALL: + case Call::GLOBAL_CALL: { + // Receiver is undefined for global calls. + builder().LoadUndefined().StoreAccumulatorInRegister(receiver); + // Load callee as a global variable. + VariableProxy* proxy = callee_expr->AsVariableProxy(); + VisitVariableLoad(proxy->var()); + builder().StoreAccumulatorInRegister(callee); + break; + } case Call::LOOKUP_SLOT_CALL: case Call::SUPER_CALL: case Call::POSSIBLY_EVAL_CALL: diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index a192bc6641..99536c33fb 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -29,6 +29,7 @@ class BytecodeGenerator : public AstVisitor { void VisitArithmeticExpression(BinaryOperation* binop); void VisitPropertyLoad(Register obj, Property* expr); + void VisitVariableLoad(Variable* variable); inline BytecodeArrayBuilder& builder() { return builder_; } inline Scope* scope() const { return scope_; } diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index f4756516f1..5b63eec480 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -36,6 +36,9 @@ namespace interpreter { V(LdaTrue, OperandType::kNone) \ V(LdaFalse, OperandType::kNone) \ \ + /* Load globals */ \ + V(LdaGlobal, OperandType::kIdx) \ + \ /* Register-accumulator transfers */ \ V(Ldar, OperandType::kReg) \ V(Star, OperandType::kReg) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 61cd7a44c0..63b2c94758 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -191,6 +191,18 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) { } +// LdaGlobal +// +// Load the global at |slot_index| into the accumulator. +void Interpreter::DoLdaGlobal(compiler::InterpreterAssembler* assembler) { + Node* slot_index = __ BytecodeOperandIdx(0); + Node* smi_slot_index = __ SmiTag(slot_index); + Node* result = __ CallRuntime(Runtime::kLoadGlobalViaContext, smi_slot_index); + __ SetAccumulator(result); + __ Dispatch(); +} + + void Interpreter::DoPropertyLoadIC(Callable ic, compiler::InterpreterAssembler* assembler) { Node* code_target = __ HeapConstant(ic.code()); diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index bff48bcce1..5c6cea0167 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -5,6 +5,7 @@ #include "src/v8.h" #include "src/compiler.h" +#include "src/interpreter/bytecode-array-iterator.h" #include "src/interpreter/bytecode-generator.h" #include "src/interpreter/interpreter.h" #include "test/cctest/cctest.h" @@ -57,6 +58,13 @@ class BytecodeGeneratorHelper { }; +// Helper macros for handcrafting bytecode sequences. +#define B(x) static_cast(Bytecode::k##x) +#define U8(x) static_cast((x) & 0xff) +#define R(x) static_cast(-(x) & 0xff) +#define _ static_cast(0x5a) + + // Structure for containing expected bytecode snippets. template struct ExpectedSnippet { @@ -70,17 +78,82 @@ struct ExpectedSnippet { }; -// Helper macros for handcrafting bytecode sequences. -#define B(x) static_cast(Bytecode::k##x) -#define U8(x) static_cast((x) & 0xff) -#define R(x) static_cast(-(x) & 0xff) +static void CheckConstant(int expected, Object* actual) { + CHECK_EQ(expected, Smi::cast(actual)->value()); +} + + +static void CheckConstant(double expected, Object* actual) { + CHECK_EQ(expected, HeapNumber::cast(actual)->value()); +} + + +static void CheckConstant(const char* expected, Object* actual) { + Handle expected_string = + CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(expected); + CHECK(String::cast(actual)->Equals(*expected_string)); +} + + +template +static void CheckBytecodeArrayEqual(struct ExpectedSnippet expected, + Handle actual, + bool has_unknown = false) { + CHECK_EQ(actual->frame_size(), expected.frame_size); + CHECK_EQ(actual->parameter_count(), expected.parameter_count); + CHECK_EQ(actual->length(), expected.bytecode_length); + if (expected.constant_count == 0) { + CHECK_EQ(actual->constant_pool(), CcTest::heap()->empty_fixed_array()); + } else { + CHECK_EQ(actual->constant_pool()->length(), expected.constant_count); + for (int i = 0; i < expected.constant_count; i++) { + CheckConstant(expected.constants[i], actual->constant_pool()->get(i)); + } + } + + BytecodeArrayIterator iterator(actual); + int i = 0; + while (!iterator.done()) { + int bytecode_index = i++; + Bytecode bytecode = iterator.current_bytecode(); + if (Bytecodes::ToByte(bytecode) != expected.bytecode[bytecode_index]) { + std::ostringstream stream; + stream << "Check failed: expected bytecode [" << bytecode_index + << "] to be " << Bytecodes::ToString(static_cast( + expected.bytecode[bytecode_index])) + << " but got " << Bytecodes::ToString(bytecode); + FATAL(stream.str().c_str()); + } + for (int j = 0; j < Bytecodes::NumberOfOperands(bytecode); ++j, ++i) { + uint8_t raw_operand = + iterator.GetRawOperand(j, Bytecodes::GetOperandType(bytecode, j)); + if (has_unknown) { + // Check actual bytecode array doesn't have the same byte as the + // one we use to specify an unknown byte. + CHECK_NE(raw_operand, _); + if (expected.bytecode[i] == _) { + continue; + } + } + if (raw_operand != expected.bytecode[i]) { + std::ostringstream stream; + stream << "Check failed: expected operand [" << j << "] for bytecode [" + << bytecode_index << "] to be " + << static_cast(expected.bytecode[i]) << " but got " + << static_cast(raw_operand); + FATAL(stream.str().c_str()); + } + } + iterator.Advance(); + } +} TEST(PrimitiveReturnStatements) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; - ExpectedSnippet snippets[] = { + ExpectedSnippet snippets[] = { {"", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0}, {"return;", 0, 1, 2, {B(LdaUndefined), B(Return)}, 0}, {"return null;", 0, 1, 2, {B(LdaNull), B(Return)}, 0}, @@ -95,14 +168,9 @@ TEST(PrimitiveReturnStatements) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array()); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -111,7 +179,7 @@ TEST(PrimitiveExpressions) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; - ExpectedSnippet snippets[] = { + ExpectedSnippet snippets[] = { {"var x = 0; return x;", kPointerSize, 1, @@ -142,14 +210,9 @@ TEST(PrimitiveExpressions) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array()); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -158,7 +221,7 @@ TEST(Parameters) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; - ExpectedSnippet snippets[] = { + ExpectedSnippet snippets[] = { {"function f() { return this; }", 0, 1, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0}, {"function f(arg1) { return arg1; }", @@ -173,14 +236,9 @@ TEST(Parameters) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunction(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool(), CcTest::heap()->empty_fixed_array()); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -219,18 +277,9 @@ TEST(Constants) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - CHECK_EQ(Smi::cast(ba->constant_pool()->get(j))->value(), - snippets[i].constants[j]); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -268,18 +317,9 @@ TEST(Constants) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - CHECK_EQ(HeapNumber::cast(ba->constant_pool()->get(j))->value(), - snippets[i].constants[j]); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -315,19 +355,9 @@ TEST(Constants) { size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - Handle expected = helper.factory()->NewStringFromAsciiChecked( - snippets[i].constants[j]); - CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected)); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } } @@ -405,19 +435,9 @@ TEST(PropertyLoads) { }; size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, "f"); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - Handle expected = helper.factory()->NewStringFromAsciiChecked( - snippets[i].constants[j]); - CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected)); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -509,19 +529,9 @@ TEST(PropertyStores) { }; size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, "f"); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - Handle expected = - helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]); - CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected)); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); } } @@ -592,19 +602,84 @@ TEST(PropertyCall) { }; size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); for (size_t i = 0; i < num_snippets; i++) { - Handle ba = + Handle bytecode_array = helper.MakeBytecode(snippets[i].code_snippet, "f"); - CHECK_EQ(ba->frame_size(), snippets[i].frame_size); - CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count); - CHECK_EQ(ba->length(), snippets[i].bytecode_length); - CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode, - ba->length())); - CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count); - for (int j = 0; j < snippets[i].constant_count; j++) { - Handle expected = - helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]); - CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected)); - } + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + +TEST(LoadGlobal) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet snippets[] = { + {"var a = 1;\nfunction f() { return a; }\nf()", + 0, 1, 3, + { + B(LdaGlobal), _, + B(Return) + }, + }, + {"function t() { }\nfunction f() { return t; }\nf()", + 0, 1, 3, + { + B(LdaGlobal), _, + B(Return) + }, + }, + }; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + Handle bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + bytecode_array->Print(); + CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); + } +} + + +TEST(CallGlobal) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet snippets[] = { + {"function t() { }\nfunction f() { return t(); }\nf()", + 2 * kPointerSize, 1, 12, + { + B(LdaUndefined), + B(Star), R(1), + B(LdaGlobal), _, + B(Star), R(0), + B(Call), R(0), R(1), U8(0), + B(Return) + }, + }, + {"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()", + 5 * kPointerSize, 1, 24, + { + B(LdaUndefined), + B(Star), R(1), + B(LdaGlobal), _, + B(Star), R(0), + B(LdaSmi8), U8(1), + B(Star), R(2), + B(LdaSmi8), U8(2), + B(Star), R(3), + B(LdaSmi8), U8(3), + B(Star), R(4), + B(Call), R(0), R(1), U8(3), + B(Return) + }, + }, + }; + + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + Handle bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array, true); } } diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index b4a2eb3b9c..c92ca13b32 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -50,19 +50,41 @@ class InterpreterCallable { }; +static const char* kFunctionName = "f"; + + class InterpreterTester { public: - InterpreterTester(Isolate* isolate, Handle bytecode, - MaybeHandle feedback_vector = - MaybeHandle()) + InterpreterTester(Isolate* isolate, const char* source, + MaybeHandle bytecode, + MaybeHandle feedback_vector) : isolate_(isolate), + source_(source), bytecode_(bytecode), feedback_vector_(feedback_vector) { i::FLAG_vector_stores = true; i::FLAG_ignition = true; + i::FLAG_always_opt = false; + // Set ignition filter flag via SetFlagsFromString to avoid double-free + // (or potential leak with StrDup() based on ownership confusion). + ScopedVector ignition_filter(64); + SNPrintF(ignition_filter, "--ignition-filter=%s", kFunctionName); + FlagList::SetFlagsFromString(ignition_filter.start(), + ignition_filter.length()); // Ensure handler table is generated. isolate->interpreter()->Initialize(); } + + InterpreterTester(Isolate* isolate, Handle bytecode, + MaybeHandle feedback_vector = + MaybeHandle()) + : InterpreterTester(isolate, nullptr, bytecode, feedback_vector) {} + + + InterpreterTester(Isolate* isolate, const char* source) + : InterpreterTester(isolate, source, MaybeHandle(), + MaybeHandle()) {} + virtual ~InterpreterTester() {} template @@ -70,28 +92,49 @@ class InterpreterTester { return InterpreterCallable(isolate_, GetBytecodeFunction()); } - Handle NewObject(const char* script) { + static Handle NewObject(const char* script) { return v8::Utils::OpenHandle(*CompileRun(script)); } + static Handle GetName(Isolate* isolate, const char* name) { + Handle result = isolate->factory()->NewStringFromAsciiChecked(name); + return isolate->factory()->string_table()->LookupString(isolate, result); + } + + static std::string function_name() { + return std::string(kFunctionName); + } + private: Isolate* isolate_; - Handle bytecode_; + const char* source_; + MaybeHandle bytecode_; MaybeHandle feedback_vector_; template Handle GetBytecodeFunction() { - int arg_count = sizeof...(A); - std::string function_text("(function("); - for (int i = 0; i < arg_count; i++) { - function_text += i == 0 ? "a" : ", a"; + Handle function; + if (source_) { + CompileRun(source_); + Local api_function = + Local::Cast(CcTest::global()->Get(v8_str(kFunctionName))); + function = v8::Utils::OpenHandle(*api_function); + } else { + int arg_count = sizeof...(A); + std::string source("(function " + function_name() + "("); + for (int i = 0; i < arg_count; i++) { + source += i == 0 ? "a" : ", a"; + } + source += "){})"; + function = v8::Utils::OpenHandle( + *v8::Handle::Cast(CompileRun(source.c_str()))); + function->ReplaceCode( + *isolate_->builtins()->InterpreterEntryTrampoline()); } - function_text += "){})"; - Handle function = v8::Utils::OpenHandle( - *v8::Handle::Cast(CompileRun(function_text.c_str()))); - function->ReplaceCode(*isolate_->builtins()->InterpreterEntryTrampoline()); - function->shared()->set_function_data(*bytecode_); + if (!bytecode_.is_null()) { + function->shared()->set_function_data(*bytecode_.ToHandleChecked()); + } if (!feedback_vector_.is_null()) { function->shared()->set_feedback_vector( *feedback_vector_.ToHandleChecked()); @@ -470,6 +513,40 @@ TEST(InterpreterParameter8) { } +TEST(InterpreterLoadGlobal) { + HandleAndZoneScope handles; + + // Test loading a global. + std::string source( + "var global = 321;\n" + "function " + InterpreterTester::function_name() + "() {\n" + " return global;\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(321)); +} + + +TEST(InterpreterCallGlobal) { + HandleAndZoneScope handles; + + // Test calling a global function. + std::string source( + "function g_add(a, b) { return a + b; }\n" + "function " + InterpreterTester::function_name() + "() {\n" + " return g_add(5, 10);\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable<>(); + + Handle return_val = callable().ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(15)); +} + + TEST(InterpreterLoadNamedProperty) { HandleAndZoneScope handles; i::Isolate* isolate = handles.main_isolate(); @@ -495,7 +572,7 @@ TEST(InterpreterLoadNamedProperty) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject("({ val : 123 })"); + Handle object = InterpreterTester::NewObject("({ val : 123 })"); // Test IC miss. Handle return_val = callable(object).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123)); @@ -505,16 +582,20 @@ TEST(InterpreterLoadNamedProperty) { CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123)); // Test transition to polymorphic IC. - Handle object2 = tester.NewObject("({ val : 456, other : 123 })"); + Handle object2 = + InterpreterTester::NewObject("({ val : 456, other : 123 })"); return_val = callable(object2).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(456)); // Test transition to megamorphic IC. - Handle object3 = tester.NewObject("({ val : 789, val2 : 123 })"); + Handle object3 = + InterpreterTester::NewObject("({ val : 789, val2 : 123 })"); callable(object3).ToHandleChecked(); - Handle object4 = tester.NewObject("({ val : 789, val3 : 123 })"); + Handle object4 = + InterpreterTester::NewObject("({ val : 789, val3 : 123 })"); callable(object4).ToHandleChecked(); - Handle object5 = tester.NewObject("({ val : 789, val4 : 123 })"); + Handle object5 = + InterpreterTester::NewObject("({ val : 789, val4 : 123 })"); return_val = callable(object5).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789)); } @@ -545,7 +626,7 @@ TEST(InterpreterLoadKeyedProperty) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject("({ key : 123 })"); + Handle object = InterpreterTester::NewObject("({ key : 123 })"); // Test IC miss. Handle return_val = callable(object).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123)); @@ -555,7 +636,8 @@ TEST(InterpreterLoadKeyedProperty) { CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123)); // Test transition to megamorphic IC. - Handle object3 = tester.NewObject("({ key : 789, val2 : 123 })"); + Handle object3 = + InterpreterTester::NewObject("({ key : 789, val2 : 123 })"); return_val = callable(object3).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789)); } @@ -587,7 +669,7 @@ TEST(InterpreterStoreNamedProperty) { InterpreterTester tester(isolate, bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject("({ val : 123 })"); + Handle object = InterpreterTester::NewObject("({ val : 123 })"); // Test IC miss. Handle result; callable(object).ToHandleChecked(); @@ -600,17 +682,21 @@ TEST(InterpreterStoreNamedProperty) { CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); // Test transition to polymorphic IC. - Handle object2 = tester.NewObject("({ val : 456, other : 123 })"); + Handle object2 = + InterpreterTester::NewObject("({ val : 456, other : 123 })"); callable(object2).ToHandleChecked(); CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result)); CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); // Test transition to megamorphic IC. - Handle object3 = tester.NewObject("({ val : 789, val2 : 123 })"); + Handle object3 = + InterpreterTester::NewObject("({ val : 789, val2 : 123 })"); callable(object3).ToHandleChecked(); - Handle object4 = tester.NewObject("({ val : 789, val3 : 123 })"); + Handle object4 = + InterpreterTester::NewObject("({ val : 789, val3 : 123 })"); callable(object4).ToHandleChecked(); - Handle object5 = tester.NewObject("({ val : 789, val4 : 123 })"); + Handle object5 = + InterpreterTester::NewObject("({ val : 789, val4 : 123 })"); callable(object5).ToHandleChecked(); CHECK(Runtime::GetObjectProperty(isolate, object5, name).ToHandle(&result)); CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); @@ -643,7 +729,7 @@ TEST(InterpreterStoreKeyedProperty) { InterpreterTester tester(isolate, bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject("({ val : 123 })"); + Handle object = InterpreterTester::NewObject("({ val : 123 })"); // Test IC miss. Handle result; callable(object).ToHandleChecked(); @@ -656,7 +742,8 @@ TEST(InterpreterStoreKeyedProperty) { CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); // Test transition to megamorphic IC. - Handle object2 = tester.NewObject("({ val : 456, other : 123 })"); + Handle object2 = + InterpreterTester::NewObject("({ val : 456, other : 123 })"); callable(object2).ToHandleChecked(); CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result)); CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); @@ -692,7 +779,7 @@ TEST(InterpreterCall) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject( + Handle object = InterpreterTester::NewObject( "new (function Obj() { this.func = function() { return 0x265; }})()"); Handle return_val = callable(object).ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x265)); @@ -714,7 +801,7 @@ TEST(InterpreterCall) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject( + Handle object = InterpreterTester::NewObject( "new (function Obj() {" " this.val = 1234;" " this.func = function() { return this.val; };" @@ -745,7 +832,7 @@ TEST(InterpreterCall) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject( + Handle object = InterpreterTester::NewObject( "new (function Obj() { " " this.func = function(a, b) { return a - b; }" "})()"); @@ -791,7 +878,7 @@ TEST(InterpreterCall) { InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); auto callable = tester.GetCallable>(); - Handle object = tester.NewObject( + Handle object = InterpreterTester::NewObject( "new (function Obj() { " " this.prefix = \"prefix_\";" " this.func = function(a, b, c, d, e, f, g, h, i, j) {" diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index 357aa673c5..35a7663208 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -39,6 +39,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { Register reg(0); builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg); + // Emit global load operations. + builder.LoadGlobal(1); + // Emit load / store property operations. builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY) .LoadKeyedProperty(reg, 0, LanguageMode::SLOPPY)