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() {
void BytecodeGraphBuilder::VisitCreateClosureWide() { VisitCreateClosure(); }
void BytecodeGraphBuilder::BuildCreateArguments(
CreateArgumentsParameters::Type type) {
CreateArgumentsParameters::Type type, int rest_index) {
FrameStateBeforeAndAfter states(this);
const Operator* op = javascript()->CreateArguments(type, 0);
const Operator* op = javascript()->CreateArguments(type, rest_index);
Node* object = NewNode(op, GetFunctionClosure());
environment()->BindAccumulator(object, &states);
}
void BytecodeGraphBuilder::VisitCreateMappedArguments() {
BuildCreateArguments(CreateArgumentsParameters::kMappedArguments);
BuildCreateArguments(CreateArgumentsParameters::kMappedArguments, 0);
}
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) {
......
......@@ -122,7 +122,7 @@ class BytecodeGraphBuilder {
void BuildCreateRegExpLiteral();
void BuildCreateArrayLiteral();
void BuildCreateObjectLiteral();
void BuildCreateArguments(CreateArgumentsParameters::Type type);
void BuildCreateArguments(CreateArgumentsParameters::Type type, int index);
void BuildLoadGlobal(TypeofMode typeof_mode);
void BuildStoreGlobal();
void BuildNamedLoad();
......
......@@ -575,6 +575,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
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(
Handle<String> pattern, int literal_index, int flags) {
......
......@@ -148,6 +148,9 @@ class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
// Create a new arguments object in the accumulator.
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.
BytecodeArrayBuilder& CreateRegExpLiteral(Handle<String> pattern,
int literal_index, int flags);
......
......@@ -584,11 +584,10 @@ void BytecodeGenerator::MakeBytecodeBody() {
// Build the arguments object if it is used.
VisitArgumentsObject(scope()->arguments());
// TODO(mythria): Build rest arguments array if it is used.
// Build rest arguments array if it is used.
int rest_index;
if (scope()->rest_parameter(&rest_index)) {
UNIMPLEMENTED();
}
Variable* rest_parameter = scope()->rest_parameter(&rest_index);
VisitRestArgumentsArray(rest_parameter, rest_index);
// Build assignment to {.this_function} variable if it is used.
VisitThisFunctionVariable(scope()->this_function_var());
......@@ -2451,6 +2450,15 @@ void BytecodeGenerator::VisitArgumentsObject(Variable* variable) {
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) {
if (variable == nullptr) return;
......
......@@ -77,6 +77,7 @@ class BytecodeGenerator final : public AstVisitor {
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest, int index);
void VisitThisFunctionVariable(Variable* variable);
void VisitNewTargetVariable(Variable* variable);
void VisitNewLocalFunctionContext();
......
......@@ -225,6 +225,7 @@ namespace interpreter {
/* Arguments allocation */ \
V(CreateMappedArguments, OperandType::kNone) \
V(CreateUnmappedArguments, OperandType::kNone) \
V(CreateRestArguments, OperandType::kIdx8) \
\
/* Control Flow */ \
V(Jump, OperandType::kImm8) \
......
......@@ -1774,6 +1774,19 @@ void Interpreter::DoCreateUnmappedArguments(
__ 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
//
......
......@@ -650,7 +650,6 @@
'test-heap/CompilationCacheCachingBehavior': [FAIL],
'test-heap/CellsInOptimizedCodeAreWeak': [FAIL],
'test-run-inlining/InlineTwice': [FAIL],
'test-run-jsobjects/ArgumentsRest': [FAIL],
'test-decls/Regress425510': [FAIL],
# TODO(rmcilroy,4680): Fail on some bot configurations.
......
......@@ -1679,6 +1679,10 @@ TEST(BytecodeGraphBuilderCreateArgumentsNoParameters) {
{factory->undefined_value()}},
{"function f(a) {'use strict'; return arguments[0];}",
{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]);
......@@ -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) {
HandleAndZoneScope scope;
......
......@@ -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) {
InitializedHandleScope handle_scope;
......
......@@ -2214,6 +2214,15 @@ TEST(InterpreterCreateArguments) {
std::make_pair("function f(a, b, c, d) {"
" 'use strict'; c = b; return arguments[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.
......
......@@ -651,13 +651,6 @@
'language/object-literal/getter': [SKIP],
'language/object-literal/method': [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/statements/do-while/S12.6.1_A4_T5': [SKIP],
'language/statements/let/block-local-closure-get-before-initialization': [SKIP],
......
......@@ -27,6 +27,12 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
CHECK_EQ(builder.context_count(), 1);
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.
builder.LoadLiteral(Smi::FromInt(0))
.LoadLiteral(Smi::FromInt(8))
......@@ -88,10 +94,6 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
false);
builder.CreateClosure(shared_info, NOT_TENURED);
// Emit argument creation operations.
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
.CreateArguments(CreateArgumentsType::kUnmappedArguments);
// Emit literal creation operations.
builder.CreateRegExpLiteral(factory->NewStringFromStaticChars("a"), 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