From 66e593733742cd929df02597573d92a4f7bca353 Mon Sep 17 00:00:00 2001 From: rmcilroy Date: Tue, 13 Oct 2015 02:39:55 -0700 Subject: [PATCH] [Interpreter] Add function literal support. Adds function literal support and add support for OTHER_CALLS which can be made when calling a function literal. Adds the CreateClosure bytecode. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1396693003 Cr-Commit-Position: refs/heads/master@{#31231} --- src/compiler/bytecode-graph-builder.cc | 6 ++ src/interpreter/bytecode-array-builder.cc | 8 +++ src/interpreter/bytecode-array-builder.h | 3 + src/interpreter/bytecode-generator.cc | 16 ++++- src/interpreter/bytecodes.h | 3 + src/interpreter/interpreter.cc | 17 +++++ src/runtime/runtime-interpreter.cc | 10 +++ src/runtime/runtime.h | 3 +- .../interpreter/test-bytecode-generator.cc | 62 +++++++++++++++++++ test/cctest/interpreter/test-interpreter.cc | 17 +++++ .../bytecode-array-builder-unittest.cc | 3 + 11 files changed, 145 insertions(+), 3 deletions(-) diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index 575aa4608e..e001125921 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -315,6 +315,12 @@ void BytecodeGraphBuilder::VisitKeyedStoreICStrict( } +void BytecodeGraphBuilder::VisitCreateClosure( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::VisitCall( const interpreter::BytecodeArrayIterator& iterator) { UNIMPLEMENTED(); diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index ccebcaaf44..5476a475f2 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -322,6 +322,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( } +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure( + PretenureFlag tenured) { + DCHECK(FitsInImm8Operand(tenured)); + Output(Bytecode::kCreateClosure, static_cast(tenured)); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() { if (LastBytecodeInSameBlock()) { // If the previous bytecode puts a boolean in the accumulator diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index fd49267154..e5025a72f3 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -72,6 +72,9 @@ class BytecodeArrayBuilder { int feedback_slot, LanguageMode language_mode); + // Create a new closure for the SharedFunctionInfo in the accumulator. + BytecodeArrayBuilder& CreateClosure(PretenureFlag tenured); + // Call a JS function. The JSFunction or Callable to be called should be in // |callable|, the receiver should be in |receiver| and all subsequent // arguments should be in registers to diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 423c438cf2..6fbb46e722 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -394,7 +394,14 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - UNIMPLEMENTED(); + // Find or build a shared function info. + Handle shared_info = + Compiler::GetSharedFunctionInfo(expr, info()->script(), info()); + CHECK(!shared_info.is_null()); // TODO(rmcilroy): Set stack overflow? + + builder() + ->LoadLiteral(shared_info) + .CreateClosure(expr->pretenure() ? TENURED : NOT_TENURED); } @@ -679,10 +686,15 @@ void BytecodeGenerator::VisitCall(Call* expr) { builder()->StoreAccumulatorInRegister(callee); break; } + case Call::OTHER_CALL: { + builder()->LoadUndefined().StoreAccumulatorInRegister(receiver); + Visit(callee_expr); + builder()->StoreAccumulatorInRegister(callee); + break; + } case Call::LOOKUP_SLOT_CALL: case Call::SUPER_CALL: case Call::POSSIBLY_EVAL_CALL: - case Call::OTHER_CALL: UNIMPLEMENTED(); } diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index f8a32d806a..cc2ebea5d7 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -105,6 +105,9 @@ namespace interpreter { /* Cast operators */ \ V(ToBoolean, OperandType::kNone) \ \ + /* Closure allocation */ \ + V(CreateClosure, OperandType::kImm8) \ + \ /* Control Flow */ \ V(Jump, OperandType::kImm8) \ V(JumpConstant, OperandType::kIdx8) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 0306e7b575..3ed234c778 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -687,6 +687,23 @@ void Interpreter::DoJumpIfFalseConstant( } +// CreateClosure +// +// Creates a new closure for SharedFunctionInfo in the accumulator with the +// PretenureFlag . +void Interpreter::DoCreateClosure(compiler::InterpreterAssembler* assembler) { + // TODO(rmcilroy): Possibly call FastNewClosureStub when possible instead of + // calling into the runtime. + Node* shared = __ GetAccumulator(); + Node* tenured_raw = __ BytecodeOperandImm8(0); + Node* tenured = __ SmiTag(tenured_raw); + Node* result = + __ CallRuntime(Runtime::kInterpreterNewClosure, shared, tenured); + __ SetAccumulator(result); + __ Dispatch(); +} + + // Return // // Return the value in the accumulator. diff --git a/src/runtime/runtime-interpreter.cc b/src/runtime/runtime-interpreter.cc index 2caa2d5c9e..96ebe5d4d3 100644 --- a/src/runtime/runtime-interpreter.cc +++ b/src/runtime/runtime-interpreter.cc @@ -137,5 +137,15 @@ RUNTIME_FUNCTION(Runtime_InterpreterTypeOf) { } +RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) { + HandleScope scope(isolate); + DCHECK_EQ(2, args.length()); + CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 0); + CONVERT_SMI_ARG_CHECKED(pretenured_flag, 1); + Handle context(isolate->context(), isolate); + return *isolate->factory()->NewFunctionFromSharedFunctionInfo( + shared, context, static_cast(pretenured_flag)); +} + } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 49d22291f5..acd4a504b0 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -227,7 +227,8 @@ namespace internal { F(InterpreterGreaterThanOrEqual, 2, 1) \ F(InterpreterToBoolean, 1, 1) \ F(InterpreterLogicalNot, 1, 1) \ - F(InterpreterTypeOf, 1, 1) + F(InterpreterTypeOf, 1, 1) \ + F(InterpreterNewClosure, 2, 1) #define FOR_EACH_INTRINSIC_FUNCTION(F) \ diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index ab85a76206..e054f97099 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -121,6 +121,11 @@ static void CheckConstant(Handle expected, Object* actual) { } +static void CheckConstant(InstanceType expected, Object* actual) { + CHECK_EQ(expected, HeapObject::cast(actual)->map()->instance_type()); +} + + template static void CheckBytecodeArrayEqual(struct ExpectedSnippet expected, Handle actual, @@ -1813,6 +1818,63 @@ TEST(UnaryOperators) { } +TEST(FunctionLiterals) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + ExpectedSnippet snippets[] = { + {"return function(){ }", + 0, + 1, + 5, + { + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return (function(){ })()", + 2 * kPointerSize, + 1, + 14, + { + B(LdaUndefined), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(Call), R(0), R(1), U8(0), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + {"return (function(x){ return x; })(1)", + 3 * kPointerSize, + 1, + 18, + { + B(LdaUndefined), // + B(Star), R(1), // + B(LdaConstant), U8(0), // + B(CreateClosure), U8(0), // + B(Star), R(0), // + B(LdaSmi8), U8(1), // + B(Star), R(2), // + B(Call), R(0), R(1), U8(1), // + B(Return) // + }, + 1, + {InstanceType::SHARED_FUNCTION_INFO_TYPE}}, + }; + + for (size_t i = 0; i < arraysize(snippets); i++) { + Handle bytecode_array = + helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index fa9f89128b..188b3e61d9 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -1620,3 +1620,20 @@ TEST(InterpreterCallRuntime) { Handle return_val = callable().ToHandleChecked(); CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55)); } + + +TEST(InterpreterFunctionLiteral) { + HandleAndZoneScope handles; + + // Test calling a function literal. + std::string source( + "function " + InterpreterTester::function_name() + "(a) {\n" + " return (function(x){ return x + 2; })(a);\n" + "}"); + InterpreterTester tester(handles.main_isolate(), source.c_str()); + auto callable = tester.GetCallable>(); + + Handle return_val = callable( + Handle(Smi::FromInt(3), handles.main_isolate())).ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(5)); +} diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index c5e003a4fb..870c26d64a 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -57,6 +57,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .StoreNamedProperty(reg, reg, 0, LanguageMode::STRICT) .StoreKeyedProperty(reg, reg, 0, LanguageMode::STRICT); + // Emit closure operations. + builder.CreateClosure(NOT_TENURED); + // Call operations. builder.Call(reg, reg, 0); builder.CallRuntime(Runtime::kIsArray, reg, 1);