Commit 6256e1dc authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Fill out function prologue support.

Fills out some more of the function prologue support in the
interpreter. Deals with creation of arguments objects and throwing
IllegalRedeclarations if necessary. Also adds (untested) support for
this.function and new.target variable assignment.

Also fixes a bug in Frames::is_java_script() to deal with
interpreter frames correctly.

Cleans up comments in builtins InterpreterEntryTrampoline about
missing prologue support.

Adds the following bytecodes:
  - CreateArgumentsSloppy
  - CreateArgumentsStrict

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1412953007

Cr-Commit-Position: refs/heads/master@{#31486}
parent 4e0d1143
......@@ -910,17 +910,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -936,17 +936,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -351,6 +351,18 @@ void BytecodeGraphBuilder::VisitCreateClosure(
}
void BytecodeGraphBuilder::VisitCreateMappedArguments(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitCreateUnmappedArguments(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitCreateRegExpLiteral(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
......
......@@ -248,7 +248,8 @@ class StackFrame BASE_EMBEDDED {
bool is_java_script() const {
Type type = this->type();
return (type == JAVA_SCRIPT) || (type == OPTIMIZED);
return (type == JAVA_SCRIPT) || (type == OPTIMIZED) ||
(type == INTERPRETED);
}
// Accessors.
......
......@@ -646,17 +646,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -389,6 +389,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
CreateArgumentsType type) {
// TODO(rmcilroy): Consider passing the type as a bytecode operand rather
// than having two different bytecodes once we have better support for
// branches in the InterpreterAssembler.
Bytecode bytecode = BytecodeForCreateArguments(type);
Output(bytecode);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
int literal_index, Register flags) {
if (FitsInIdx8Operand(literal_index)) {
......@@ -989,6 +1000,21 @@ Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal(
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
CreateArgumentsType type) {
switch (type) {
case CreateArgumentsType::kMappedArguments:
return Bytecode::kCreateMappedArguments;
case CreateArgumentsType::kUnmappedArguments:
return Bytecode::kCreateUnmappedArguments;
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
return kMinUInt8 <= value && value <= kMaxUInt8;
......
......@@ -23,6 +23,10 @@ namespace interpreter {
class BytecodeLabel;
class Register;
// TODO(rmcilroy): Unify this with CreateArgumentsParameters::Type in Turbofan
// when rest parameters implementation has settled down.
enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments };
class BytecodeArrayBuilder {
public:
BytecodeArrayBuilder(Isolate* isolate, Zone* zone);
......@@ -111,6 +115,9 @@ class BytecodeArrayBuilder {
// Create a new closure for the SharedFunctionInfo in the accumulator.
BytecodeArrayBuilder& CreateClosure(PretenureFlag tenured);
// Create a new arguments object in the accumulator.
BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type);
// Literals creation. Constant elements should be in the accumulator.
BytecodeArrayBuilder& CreateRegExpLiteral(int literal_index, Register flags);
BytecodeArrayBuilder& CreateArrayLiteral(int literal_index, int flags);
......@@ -198,6 +205,7 @@ class BytecodeArrayBuilder {
static Bytecode BytecodeForKeyedStoreIC(LanguageMode language_mode);
static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode);
static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode);
static Bytecode BytecodeForCreateArguments(CreateArgumentsType type);
static bool FitsInIdx8Operand(int value);
static bool FitsInIdx8Operand(size_t value);
......
......@@ -55,7 +55,6 @@ class BytecodeGenerator::ContextScope BASE_EMBEDDED {
for (int i = depth; i > 0; --i) {
previous = previous->outer_;
}
DCHECK_EQ(previous->scope_, scope);
return previous;
}
......@@ -309,6 +308,26 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
void BytecodeGenerator::MakeBytecodeBody() {
// Build the arguments object if it is used.
VisitArgumentsObject(scope()->arguments());
// Build assignment to {.this_function} variable if it is used.
VisitThisFunctionVariable(scope()->this_function_var());
// Build assignment to {new.target} variable if it is used.
VisitNewTargetVariable(scope()->new_target_var());
// TODO(rmcilroy): Emit tracing call if requested to do so.
if (FLAG_trace) {
UNIMPLEMENTED();
}
// Visit illegal re-declaration and bail out if it exists.
if (scope()->HasIllegalRedeclaration()) {
Visit(scope()->GetIllegalRedeclaration());
return;
}
// Visit declarations within the function scope.
VisitDeclarations(scope()->declarations());
......@@ -1653,6 +1672,46 @@ void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object,
}
void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
if (variable == nullptr) return;
DCHECK(variable->IsContextSlot() || variable->IsStackAllocated());
// Allocate and initialize a new arguments object and assign to the
// {arguments} variable.
CreateArgumentsType type =
is_strict(language_mode()) || !info()->has_simple_parameters()
? CreateArgumentsType::kUnmappedArguments
: CreateArgumentsType::kMappedArguments;
builder()->CreateArguments(type);
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
if (variable == nullptr) return;
// TODO(rmcilroy): Remove once we have tests which exercise this code path.
UNIMPLEMENTED();
// Store the closure we were called with in the this_function_var.
builder()->LoadAccumulatorWithRegister(Register::function_closure());
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
if (variable == nullptr) return;
// TODO(rmcilroy): Remove once we have tests which exercise this code path.
UNIMPLEMENTED();
// Store the closure we were called with in the this_function_var.
builder()->CallRuntime(Runtime::kGetOriginalConstructor, Register(), 0);
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitFunctionClosureForContext() {
AccumulatorResultScope accumulator_execution_result(this);
Scope* closure_scope = execution_context()->scope()->ClosureScope();
......
......@@ -65,6 +65,9 @@ class BytecodeGenerator : public AstVisitor {
Variable* variable, FeedbackVectorSlot slot);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitArgumentsObject(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void VisitNewTargetVariable(Variable* variable);
void VisitNewLocalFunctionContext();
void VisitBuildLocalActivationContext();
void VisitNewLocalBlockContext(Scope* scope);
......
......@@ -125,6 +125,10 @@ namespace interpreter {
/* Closure allocation */ \
V(CreateClosure, OperandType::kImm8) \
\
/* Arguments allocation */ \
V(CreateMappedArguments, OperandType::kNone) \
V(CreateUnmappedArguments, OperandType::kNone) \
\
/* Control Flow */ \
V(Jump, OperandType::kImm8) \
V(JumpConstant, OperandType::kIdx8) \
......
......@@ -1028,6 +1028,30 @@ void Interpreter::DoCreateClosure(compiler::InterpreterAssembler* assembler) {
}
// CreateMappedArguments
//
// Creates a new mapped arguments object.
void Interpreter::DoCreateMappedArguments(
compiler::InterpreterAssembler* assembler) {
Node* closure = __ LoadRegister(Register::function_closure());
Node* result = __ CallRuntime(Runtime::kNewSloppyArguments_Generic, closure);
__ SetAccumulator(result);
__ Dispatch();
}
// CreateUnmappedArguments
//
// Creates a new unmapped arguments object.
void Interpreter::DoCreateUnmappedArguments(
compiler::InterpreterAssembler* assembler) {
Node* closure = __ LoadRegister(Register::function_closure());
Node* result = __ CallRuntime(Runtime::kNewStrictArguments_Generic, closure);
__ SetAccumulator(result);
__ Dispatch();
}
// Throw
//
// Throws the exception in the accumulator.
......
......@@ -911,17 +911,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -907,17 +907,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -908,17 +908,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -716,17 +716,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -631,17 +631,6 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// receiver with the global proxy when called as functions (without an
// explicit receiver object).
// - Code aging of the BytecodeArray object.
// - Supporting FLAG_trace.
//
// The following items are also not done here, and will probably be done using
// explicit bytecodes instead:
// - Allocating a new local context if applicable.
// - Setting up a local binding to the this function, which is used in
// derived constructors with super calls.
// - Setting new.target if required.
// - Dealing with REST parameters (only if
// https://codereview.chromium.org/1235153006 doesn't land by then).
// - Dealing with argument objects.
// Perform stack guard check.
{
......
......@@ -3362,6 +3362,136 @@ TEST(GlobalCompoundExpressions) {
}
}
TEST(CreateArguments) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
int closure = Register::function_closure().index();
int first_context_slot = Context::MIN_CONTEXT_SLOTS;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"function f() { return arguments; }",
1 * kPointerSize,
1,
6,
{
B(CreateMappedArguments), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return), //
}},
{"function f() { return arguments[0]; }",
1 * kPointerSize,
1,
8,
{
B(CreateMappedArguments), //
B(Star), R(0), //
B(LdaZero), //
B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot)), //
B(Return), //
}},
{"function f() { 'use strict'; return arguments; }",
1 * kPointerSize,
1,
6,
{
B(CreateUnmappedArguments), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return), //
}},
{"function f(a) { return arguments[0]; }",
2 * kPointerSize,
2,
20,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(CreateMappedArguments), //
B(Star), R(0), //
B(LdaZero), //
B(KeyedLoadICSloppy), R(0), U8(vector->GetIndex(slot)), //
B(Return), //
}},
{"function f(a, b, c) { return arguments; }",
2 * kPointerSize,
4,
28,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex - 2), //
B(StaContextSlot), R(1), U8(first_context_slot + 2), //
B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex - 1), //
B(StaContextSlot), R(1), U8(first_context_slot + 1), //
B(Ldar), R(BytecodeGeneratorHelper::kLastParamIndex), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(CreateMappedArguments), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return), //
}},
{"function f(a, b, c) { 'use strict'; return arguments; }",
1 * kPointerSize,
4,
6,
{
B(CreateUnmappedArguments), //
B(Star), R(0), //
B(Ldar), R(0), //
B(Return), //
}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunction(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(IllegalRedeclaration) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"const a = 1; { var a = 2; }",
3 * kPointerSize,
1,
14,
{
B(LdaSmi8), U8(MessageTemplate::kVarRedeclaration), //
B(Star), R(1), //
B(LdaConstant), U8(0), //
B(Star), R(2), //
B(CallRuntime), U16(Runtime::kNewSyntaxError), R(1), U8(2), //
B(Throw), //
},
1,
{"a"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
......@@ -2199,3 +2199,68 @@ TEST(InterpreterGlobalCompoundExpressions) {
CHECK(return_value->SameValue(*compound_expr[i].second));
}
}
TEST(InterpreterCreateArguments) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, int> create_args[9] = {
std::make_pair("function f() { return arguments[0]; }", 0),
std::make_pair("function f(a) { return arguments[0]; }", 0),
std::make_pair("function f() { return arguments[2]; }", 2),
std::make_pair("function f(a) { return arguments[2]; }", 2),
std::make_pair("function f(a, b, c, d) { return arguments[2]; }", 2),
std::make_pair("function f(a) {"
"'use strict'; return arguments[0]; }",
0),
std::make_pair("function f(a, b, c, d) {"
"'use strict'; return arguments[2]; }",
2),
// Check arguments are mapped in sloppy mode and unmapped in strict.
std::make_pair("function f(a, b, c, d) {"
" c = b; return arguments[2]; }",
1),
std::make_pair("function f(a, b, c, d) {"
" 'use strict'; c = b; return arguments[2]; }",
2),
};
// Test passing no arguments.
for (size_t i = 0; i < arraysize(create_args); i++) {
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK(return_val.is_identical_to(factory->undefined_value()));
}
// Test passing one argument.
for (size_t i = 0; i < arraysize(create_args); i++) {
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_val =
callable(handle(Smi::FromInt(40), isolate)).ToHandleChecked();
if (create_args[i].second == 0) {
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(40));
} else {
CHECK(return_val.is_identical_to(factory->undefined_value()));
}
}
// Test passing three argument.
for (size_t i = 0; i < arraysize(create_args); i++) {
Handle<Object> args[3] = {
handle(Smi::FromInt(40), isolate),
handle(Smi::FromInt(60), isolate),
handle(Smi::FromInt(80), isolate),
};
InterpreterTester tester(handles.main_isolate(), create_args[i].first);
auto callable =
tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>();
Handle<Object> return_val =
callable(args[0], args[1], args[2]).ToHandleChecked();
CHECK(return_val->SameValue(*args[create_args[i].second]));
}
}
......@@ -68,6 +68,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit closure operations.
builder.CreateClosure(NOT_TENURED);
// Emit argument creation operations.
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
.CreateArguments(CreateArgumentsType::kUnmappedArguments);
// Emit literal creation operations
builder.CreateRegExpLiteral(0, reg)
.CreateArrayLiteral(0, 0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment