Commit 9aa612cb authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Adds support for rest parameters to interpreter.

Adds implementation and tests for rest parameters to interpreter.

BUG=v8:4280,v8:4683
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33722}
parent 11017398
...@@ -1004,19 +1004,25 @@ void BytecodeGraphBuilder::VisitCreateClosure() { ...@@ -1004,19 +1004,25 @@ void BytecodeGraphBuilder::VisitCreateClosure() {
void BytecodeGraphBuilder::VisitCreateClosureWide() { VisitCreateClosure(); } void BytecodeGraphBuilder::VisitCreateClosureWide() { VisitCreateClosure(); }
void BytecodeGraphBuilder::BuildCreateArguments( void BytecodeGraphBuilder::BuildCreateArguments(
CreateArgumentsParameters::Type type) { CreateArgumentsParameters::Type type, int rest_index) {
FrameStateBeforeAndAfter states(this); FrameStateBeforeAndAfter states(this);
const Operator* op = javascript()->CreateArguments(type, 0); const Operator* op = javascript()->CreateArguments(type, rest_index);
Node* object = NewNode(op, GetFunctionClosure()); Node* object = NewNode(op, GetFunctionClosure());
environment()->BindAccumulator(object, &states); environment()->BindAccumulator(object, &states);
} }
void BytecodeGraphBuilder::VisitCreateMappedArguments() { void BytecodeGraphBuilder::VisitCreateMappedArguments() {
BuildCreateArguments(CreateArgumentsParameters::kMappedArguments); BuildCreateArguments(CreateArgumentsParameters::kMappedArguments, 0);
} }
void BytecodeGraphBuilder::VisitCreateUnmappedArguments() { void BytecodeGraphBuilder::VisitCreateUnmappedArguments() {
BuildCreateArguments(CreateArgumentsParameters::kUnmappedArguments); BuildCreateArguments(CreateArgumentsParameters::kUnmappedArguments, 0);
}
void BytecodeGraphBuilder::VisitCreateRestArguments() {
int index =
Smi::cast(*bytecode_iterator().GetConstantForIndexOperand(0))->value();
BuildCreateArguments(CreateArgumentsParameters::kRestArray, index);
} }
void BytecodeGraphBuilder::BuildCreateLiteral(const Operator* op) { void BytecodeGraphBuilder::BuildCreateLiteral(const Operator* op) {
......
...@@ -122,7 +122,7 @@ class BytecodeGraphBuilder { ...@@ -122,7 +122,7 @@ class BytecodeGraphBuilder {
void BuildCreateRegExpLiteral(); void BuildCreateRegExpLiteral();
void BuildCreateArrayLiteral(); void BuildCreateArrayLiteral();
void BuildCreateObjectLiteral(); void BuildCreateObjectLiteral();
void BuildCreateArguments(CreateArgumentsParameters::Type type); void BuildCreateArguments(CreateArgumentsParameters::Type type, int index);
void BuildLoadGlobal(TypeofMode typeof_mode); void BuildLoadGlobal(TypeofMode typeof_mode);
void BuildStoreGlobal(); void BuildStoreGlobal();
void BuildNamedLoad(); void BuildNamedLoad();
......
...@@ -575,6 +575,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments( ...@@ -575,6 +575,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRestArguments(int index) {
size_t index_entry =
GetConstantPoolEntry(Handle<Object>(Smi::FromInt(index), isolate_));
// This will always be the first entry in the constant pool, since the rest
// arguments object is created at the start of the function just after
// creating the arguments object.
CHECK(FitsInIdx8Operand(index_entry));
Output(Bytecode::kCreateRestArguments, static_cast<uint8_t>(index_entry));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral( BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
Handle<String> pattern, int literal_index, int flags) { Handle<String> pattern, int literal_index, int flags) {
......
...@@ -148,6 +148,9 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover { ...@@ -148,6 +148,9 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
// Create a new arguments object in the accumulator. // Create a new arguments object in the accumulator.
BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type); BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type);
// Create a new rest arguments object starting at |index| in the accumulator.
BytecodeArrayBuilder& CreateRestArguments(int index);
// Literals creation. Constant elements should be in the accumulator. // Literals creation. Constant elements should be in the accumulator.
BytecodeArrayBuilder& CreateRegExpLiteral(Handle<String> pattern, BytecodeArrayBuilder& CreateRegExpLiteral(Handle<String> pattern,
int literal_index, int flags); int literal_index, int flags);
......
...@@ -584,11 +584,10 @@ void BytecodeGenerator::MakeBytecodeBody() { ...@@ -584,11 +584,10 @@ void BytecodeGenerator::MakeBytecodeBody() {
// Build the arguments object if it is used. // Build the arguments object if it is used.
VisitArgumentsObject(scope()->arguments()); VisitArgumentsObject(scope()->arguments());
// TODO(mythria): Build rest arguments array if it is used. // Build rest arguments array if it is used.
int rest_index; int rest_index;
if (scope()->rest_parameter(&rest_index)) { Variable* rest_parameter = scope()->rest_parameter(&rest_index);
UNIMPLEMENTED(); VisitRestArgumentsArray(rest_parameter, rest_index);
}
// Build assignment to {.this_function} variable if it is used. // Build assignment to {.this_function} variable if it is used.
VisitThisFunctionVariable(scope()->this_function_var()); VisitThisFunctionVariable(scope()->this_function_var());
...@@ -2451,6 +2450,15 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) { ...@@ -2451,6 +2450,15 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid());
} }
void BytecodeGenerator::VisitRestArgumentsArray(Variable* rest, int index) {
if (rest == nullptr) return;
// Allocate and initialize a new arguments object and assign to the {rest}
// variable.
builder()->CreateRestArguments(index);
DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
VisitVariableAssignment(rest, FeedbackVectorSlot::Invalid());
}
void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) { void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) {
if (variable == nullptr) return; if (variable == nullptr) return;
......
...@@ -77,6 +77,7 @@ class BytecodeGenerator final : public AstVisitor { ...@@ -77,6 +77,7 @@ class BytecodeGenerator final : public AstVisitor {
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot); void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitArgumentsObject(Variable* variable); void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest, int index);
void VisitThisFunctionVariable(Variable* variable); void VisitThisFunctionVariable(Variable* variable);
void VisitNewTargetVariable(Variable* variable); void VisitNewTargetVariable(Variable* variable);
void VisitNewLocalFunctionContext(); void VisitNewLocalFunctionContext();
......
...@@ -225,6 +225,7 @@ namespace interpreter { ...@@ -225,6 +225,7 @@ namespace interpreter {
/* Arguments allocation */ \ /* Arguments allocation */ \
V(CreateMappedArguments, OperandType::kNone) \ V(CreateMappedArguments, OperandType::kNone) \
V(CreateUnmappedArguments, OperandType::kNone) \ V(CreateUnmappedArguments, OperandType::kNone) \
V(CreateRestArguments, OperandType::kIdx8) \
\ \
/* Control Flow */ \ /* Control Flow */ \
V(Jump, OperandType::kImm8) \ V(Jump, OperandType::kImm8) \
......
...@@ -1774,6 +1774,19 @@ void Interpreter::DoCreateUnmappedArguments( ...@@ -1774,6 +1774,19 @@ void Interpreter::DoCreateUnmappedArguments(
__ Dispatch(); __ Dispatch();
} }
// CreateRestArguments
//
// Creates a new rest arguments object starting at |rest_index|.
void Interpreter::DoCreateRestArguments(
compiler::InterpreterAssembler* assembler) {
Node* closure = __ LoadRegister(Register::function_closure());
Node* constant_pool_index = __ BytecodeOperandIdx(0);
Node* rest_index = __ LoadConstantPoolEntry(constant_pool_index);
Node* result =
__ CallRuntime(Runtime::kNewRestArguments_Generic, closure, rest_index);
__ SetAccumulator(result);
__ Dispatch();
}
// Throw // Throw
// //
......
...@@ -650,7 +650,6 @@ ...@@ -650,7 +650,6 @@
'test-heap/CompilationCacheCachingBehavior': [FAIL], 'test-heap/CompilationCacheCachingBehavior': [FAIL],
'test-heap/CellsInOptimizedCodeAreWeak': [FAIL], 'test-heap/CellsInOptimizedCodeAreWeak': [FAIL],
'test-run-inlining/InlineTwice': [FAIL], 'test-run-inlining/InlineTwice': [FAIL],
'test-run-jsobjects/ArgumentsRest': [FAIL],
'test-decls/Regress425510': [FAIL], 'test-decls/Regress425510': [FAIL],
# TODO(rmcilroy,4680): Fail on some bot configurations. # TODO(rmcilroy,4680): Fail on some bot configurations.
......
...@@ -1679,6 +1679,10 @@ TEST(BytecodeGraphBuilderCreateArgumentsNoParameters) { ...@@ -1679,6 +1679,10 @@ TEST(BytecodeGraphBuilderCreateArgumentsNoParameters) {
{factory->undefined_value()}}, {factory->undefined_value()}},
{"function f(a) {'use strict'; return arguments[0];}", {"function f(a) {'use strict'; return arguments[0];}",
{factory->undefined_value()}}, {factory->undefined_value()}},
{"function f(...restArgs) {return restArgs[0];}",
{factory->undefined_value()}},
{"function f(a, ...restArgs) {return restArgs[0];}",
{factory->undefined_value()}},
}; };
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
...@@ -1741,6 +1745,49 @@ TEST(BytecodeGraphBuilderCreateArguments) { ...@@ -1741,6 +1745,49 @@ TEST(BytecodeGraphBuilderCreateArguments) {
} }
} }
TEST(BytecodeGraphBuilderCreateRestArguments) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<3> snippets[] = {
{"function f(...restArgs) {return restArgs[0];}",
{factory->NewNumberFromInt(1), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(3)}},
{"function f(a, b, ...restArgs) {return restArgs[0];}",
{factory->NewNumberFromInt(3), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(3)}},
{"function f(a, b, ...restArgs) {return arguments[2];}",
{factory->NewNumberFromInt(3), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(3)}},
{"function f(a, ...restArgs) { return restArgs[2];}",
{factory->undefined_value(), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(3)}},
{"function f(a, ...restArgs) { return arguments[0] + restArgs[1];}",
{factory->NewNumberFromInt(4), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(3)}},
{"function inline_func(a, ...restArgs) { return restArgs[0] }"
"function f(a, b, c) {return inline_func(b, c) + arguments[0];}",
{factory->NewNumberFromInt(31), factory->NewNumberFromInt(1),
factory->NewNumberFromInt(2), factory->NewNumberFromInt(30)}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "%s\n%s();", snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable =
tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>();
Handle<Object> return_value =
callable(snippets[i].parameter(0), snippets[i].parameter(1),
snippets[i].parameter(2))
.ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderRegExpLiterals) { TEST(BytecodeGraphBuilderRegExpLiterals) {
HandleAndZoneScope scope; HandleAndZoneScope scope;
......
...@@ -5529,6 +5529,100 @@ TEST(CreateArguments) { ...@@ -5529,6 +5529,100 @@ TEST(CreateArguments) {
} }
} }
TEST(CreateRestArguments) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot = feedback_spec.AddKeyedLoadICSlot();
FeedbackVectorSlot slot1 = feedback_spec.AddKeyedLoadICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
ExpectedSnippet<int> snippets[] = {
{"function f(...restArgs) { return restArgs; }",
1 * kPointerSize,
2,
5,
{
B(CreateRestArguments), U8(0), //
B(Star), R(0), //
B(Return), //
},
1,
{0}},
{"function f(a, ...restArgs) { return restArgs; }",
2 * kPointerSize,
3,
14,
{
B(CreateRestArguments), U8(0), //
B(Star), R(0), //
B(LdaTheHole), //
B(Star), R(1), //
B(Ldar), A(1, 3), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Return), //
},
1,
{1}},
{"function f(a, ...restArgs) { return restArgs[0]; }",
3 * kPointerSize,
3,
20,
{
B(CreateRestArguments), U8(0), //
B(Star), R(0), //
B(LdaTheHole), //
B(Star), R(1), //
B(Ldar), A(1, 3), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Star), R(2), //
B(LdaZero), //
B(KeyedLoadICSloppy), R(2), U8(vector->GetIndex(slot)), //
B(Return), //
},
1,
{1}},
{"function f(a, ...restArgs) { return restArgs[0] + arguments[0]; }",
5 * kPointerSize,
3,
35,
{
B(CreateUnmappedArguments), //
B(Star), R(0), //
B(CreateRestArguments), U8(0), //
B(Star), R(1), //
B(LdaTheHole), //
B(Star), R(2), //
B(Ldar), A(1, 3), //
B(Star), R(2), //
B(Ldar), R(1), //
B(Star), R(3), //
B(LdaZero), //
B(KeyedLoadICSloppy), R(3), U8(vector->GetIndex(slot)), //
B(Star), R(4), //
B(Ldar), R(0), //
B(Star), R(3), //
B(LdaZero), //
B(KeyedLoadICSloppy), R(3), U8(vector->GetIndex(slot1)), //
B(Add), R(4), //
B(Return), //
},
1,
{1}},
};
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) { TEST(IllegalRedeclaration) {
InitializedHandleScope handle_scope; InitializedHandleScope handle_scope;
......
...@@ -2214,6 +2214,15 @@ TEST(InterpreterCreateArguments) { ...@@ -2214,6 +2214,15 @@ TEST(InterpreterCreateArguments) {
std::make_pair("function f(a, b, c, d) {" std::make_pair("function f(a, b, c, d) {"
" 'use strict'; c = b; return arguments[2]; }", " 'use strict'; c = b; return arguments[2]; }",
2), 2),
// check rest parameters
std::make_pair("function f(...restArray) { return restArray[0]; }", 0),
std::make_pair("function f(a, ...restArray) { return restArray[0]; }", 1),
std::make_pair("function f(a, ...restArray) { return arguments[0]; }", 0),
std::make_pair("function f(a, ...restArray) { return arguments[1]; }", 1),
std::make_pair("function f(a, ...restArray) { return restArray[1]; }", 2),
std::make_pair("function f(a, ...arguments) { return arguments[0]; }", 1),
std::make_pair("function f(a, b, ...restArray) { return restArray[0]; }",
2),
}; };
// Test passing no arguments. // Test passing no arguments.
......
...@@ -651,13 +651,6 @@ ...@@ -651,13 +651,6 @@
'language/object-literal/getter': [SKIP], 'language/object-literal/getter': [SKIP],
'language/object-literal/method': [SKIP], 'language/object-literal/method': [SKIP],
'language/object-literal/setter': [SKIP], 'language/object-literal/setter': [SKIP],
'language/rest-parameters/arrow-function': [SKIP],
'language/rest-parameters/expected-argument-count': [SKIP],
'language/rest-parameters/no-alias-arguments': [SKIP],
'language/rest-parameters/rest-index': [SKIP],
'language/rest-parameters/rest-parameters-apply': [SKIP],
'language/rest-parameters/rest-parameters-call': [SKIP],
'language/rest-parameters/rest-parameters-produce-an-array': [SKIP],
'language/rest-parameters/with-new-target': [SKIP], 'language/rest-parameters/with-new-target': [SKIP],
'language/statements/do-while/S12.6.1_A4_T5': [SKIP], 'language/statements/do-while/S12.6.1_A4_T5': [SKIP],
'language/statements/let/block-local-closure-get-before-initialization': [SKIP], 'language/statements/let/block-local-closure-get-before-initialization': [SKIP],
......
...@@ -27,6 +27,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -27,6 +27,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
CHECK_EQ(builder.context_count(), 1); CHECK_EQ(builder.context_count(), 1);
CHECK_EQ(builder.fixed_register_count(), 132); CHECK_EQ(builder.fixed_register_count(), 132);
// Emit argument creation operations. CreateRestArguments should
// be output before any bytecodes that change constant pool.
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
.CreateArguments(CreateArgumentsType::kUnmappedArguments)
.CreateRestArguments(0);
// Emit constant loads. // Emit constant loads.
builder.LoadLiteral(Smi::FromInt(0)) builder.LoadLiteral(Smi::FromInt(0))
.LoadLiteral(Smi::FromInt(8)) .LoadLiteral(Smi::FromInt(8))
...@@ -88,10 +94,6 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -88,10 +94,6 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
false); false);
builder.CreateClosure(shared_info, NOT_TENURED); builder.CreateClosure(shared_info, NOT_TENURED);
// Emit argument creation operations.
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
.CreateArguments(CreateArgumentsType::kUnmappedArguments);
// Emit literal creation operations. // Emit literal creation operations.
builder.CreateRegExpLiteral(factory->NewStringFromStaticChars("a"), 0, 0) builder.CreateRegExpLiteral(factory->NewStringFromStaticChars("a"), 0, 0)
.CreateArrayLiteral(factory->NewFixedArray(1), 0, 0) .CreateArrayLiteral(factory->NewFixedArray(1), 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