Commit d00c4666 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[Interpreter] Add support for LOOKUP_SLOT_CALL to interpreter.

Adds support for LOOKUP_SLOT_CALL calls to the interpreter. Also changes
VisitCall to keep callee and reciever consecutive to avoid register
shuffles when performing LOOKUP_SLOT_CALL calls. Adds tests for the
interpreter and bytecode graph generator.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#33237}
parent 284010c8
......@@ -1607,16 +1607,16 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// Prepare the callee and the receiver to the function call. This depends on
// the semantics of the underlying call type.
Register callee = execution_result()->NewRegister();
// The receiver and arguments need to be allocated consecutively for
// Call(). Future optimizations could avoid this there are no
// Call(). We allocate the callee and receiver consecutively for calls to
// kLoadLookupSlot. Future optimizations could avoid this there are no
// arguments or the receiver and arguments are already consecutive.
ZoneList<Expression*>* args = expr->arguments();
execution_result()->PrepareForConsecutiveAllocations(args->length() + 1);
execution_result()->PrepareForConsecutiveAllocations(args->length() + 2);
Register callee = execution_result()->NextConsecutiveRegister();
Register receiver = execution_result()->NextConsecutiveRegister();
bool possibly_eval = false;
switch (call_type) {
case Call::NAMED_PROPERTY_CALL:
case Call::KEYED_PROPERTY_CALL: {
......@@ -1637,30 +1637,26 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->StoreAccumulatorInRegister(callee);
break;
}
case Call::LOOKUP_SLOT_CALL:
case Call::POSSIBLY_EVAL_CALL: {
possibly_eval = true;
if (callee_expr->AsVariableProxy()->var()->IsLookupSlot()) {
TemporaryRegisterScope temporary_register_scope(builder());
temporary_register_scope.PrepareForConsecutiveAllocations(2);
Register context = temporary_register_scope.NextConsecutiveRegister();
Register name = temporary_register_scope.NextConsecutiveRegister();
// Call LoadLookupSlot to get the callee and receiver.
DCHECK(Register::AreContiguous(callee, receiver));
Variable* variable = callee_expr->AsVariableProxy()->var();
builder()
->MoveRegister(Register::function_context(), context)
.LoadLiteral(variable->name())
.StoreAccumulatorInRegister(name);
// Call LoadLookupSlot to get the callee and receiver. Reuse the context
// and name arguments as the return registers (since these are
// consecutive), and them move into callee and receiver registers.
builder()
->CallRuntimeForPair(Runtime::kLoadLookupSlot, context, 2, context)
.MoveRegister(context, callee)
.MoveRegister(name, receiver);
.StoreAccumulatorInRegister(name)
.CallRuntimeForPair(Runtime::kLoadLookupSlot, context, 2, callee);
break;
}
// Fall through.
DCHECK_EQ(call_type, Call::POSSIBLY_EVAL_CALL);
}
case Call::OTHER_CALL: {
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
......@@ -1670,7 +1666,6 @@ void BytecodeGenerator::VisitCall(Call* expr) {
}
case Call::NAMED_SUPER_PROPERTY_CALL:
case Call::KEYED_SUPER_PROPERTY_CALL:
case Call::LOOKUP_SLOT_CALL:
case Call::SUPER_CALL:
UNIMPLEMENTED();
}
......@@ -1682,7 +1677,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// Resolve callee for a potential direct eval call. This block will mutate the
// callee value.
if (possibly_eval && args->length() > 0) {
if (call_type == Call::POSSIBLY_EVAL_CALL && args->length() > 0) {
TemporaryRegisterScope temporary_register_scope(builder());
temporary_register_scope.PrepareForConsecutiveAllocations(5);
Register callee_for_eval =
......
......@@ -1103,6 +1103,36 @@ TEST(BytecodeGraphBuilderLookupSlotWide) {
}
TEST(BytecodeGraphBuilderCallLookupSlot) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
ExpectedSnippet<0> snippets[] = {
{"g = function(){ return 2 }; eval(''); return g();",
{handle(Smi::FromInt(2), isolate)}},
{"g = function(){ return 2 }; eval('g = function() {return 3}');\n"
"return g();",
{handle(Smi::FromInt(3), isolate)}},
{"g = { x: function(){ return this.y }, y: 20 };\n"
"eval('g = { x: g.x, y: 30 }');\n"
"return g.x();",
{handle(Smi::FromInt(30), isolate)}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderEval) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
......
......@@ -5913,7 +5913,7 @@ TEST(Eval) {
{"return eval('1;');",
9 * kPointerSize,
1,
73,
67,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
......@@ -5928,9 +5928,7 @@ TEST(Eval) {
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
R(3), U8(2), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
......@@ -5971,7 +5969,7 @@ TEST(LookupSlot) {
{"eval('var x = 10;'); return x;",
9 * kPointerSize,
1,
75,
69,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
......@@ -5986,9 +5984,7 @@ TEST(LookupSlot) {
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
R(3), U8(2), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
......@@ -6010,7 +6006,7 @@ TEST(LookupSlot) {
{"eval('var x = 10;'); return typeof x;",
9 * kPointerSize,
1,
76,
70,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
......@@ -6025,9 +6021,7 @@ TEST(LookupSlot) {
B(LdaConstant), U8(0), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
R(3), U8(2), R(1), //
B(LdaConstant), U8(1), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
......@@ -6050,7 +6044,7 @@ TEST(LookupSlot) {
{"x = 20; return eval('');",
9 * kPointerSize,
1,
77,
71,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
......@@ -6067,9 +6061,7 @@ TEST(LookupSlot) {
B(LdaConstant), U8(1), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(3), //
B(Mov), R(3), R(1), //
B(Mov), R(4), R(2), //
R(3), U8(2), R(1), //
B(LdaConstant), U8(2), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
......@@ -6097,6 +6089,81 @@ TEST(LookupSlot) {
}
TEST(CallLookupSlot) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddCallICSlot();
USE(slot1);
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
int closure = Register::function_closure().index();
int context = Register::function_context().index();
int new_target = Register::new_target().index();
ExpectedSnippet<InstanceType> snippets[] = {
{"g = function(){}; eval(''); return g();",
9 * kPointerSize,
1,
90,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(0), //
B(Ldar), THIS(1), //
B(StaContextSlot), R(0), U8(4), //
B(CreateMappedArguments), //
B(StaContextSlot), R(0), U8(5), //
B(Ldar), R(new_target), //
B(StaContextSlot), R(0), U8(6), //
B(CreateClosure), U8(0), U8(0), //
B(StaLookupSlotSloppy), U8(1), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(2), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(1), //
B(LdaConstant), U8(3), //
B(Star), R(3), //
B(Mov), R(1), R(4), //
B(Mov), R(3), R(5), //
B(Mov), R(closure), R(6), //
B(LdaZero), //
B(Star), R(7), //
B(LdaSmi8), U8(10), //
B(Star), R(8), //
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), //
U8(5), //
B(Star), R(1), //
B(Call), R(1), R(2), U8(1), U8(0), //
B(Mov), R(context), R(3), //
B(LdaConstant), U8(1), //
B(Star), R(4), //
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlot), //
R(3), U8(2), R(1), //
B(Call), R(1), R(2), U8(0), U8(vector->GetIndex(slot2)), //
B(Return), //
},
4,
{InstanceType::SHARED_FUNCTION_INFO_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(LookupSlotInEval) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
......
......@@ -3295,6 +3295,33 @@ TEST(InterpreterLookupSlot) {
}
TEST(InterpreterCallLookupSlot) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> call_lookup[] = {
{"g = function(){ return 2 }; eval(''); return g();",
handle(Smi::FromInt(2), isolate)},
{"g = function(){ return 2 }; eval('g = function() {return 3}');\n"
"return g();",
handle(Smi::FromInt(3), isolate)},
{"g = { x: function(){ return this.y }, y: 20 };\n"
"eval('g = { x: g.x, y: 30 }');\n"
"return g.x();",
handle(Smi::FromInt(30), isolate)},
};
for (size_t i = 0; i < arraysize(call_lookup); i++) {
std::string source(InterpreterTester::SourceForBody(call_lookup[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*call_lookup[i].second));
}
}
TEST(InterpreterLookupSlotWide) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
......
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