Commit 044a62be authored by leszeks's avatar leszeks Committed by Commit bot

[interpreter] Add fast path for dynamic global lookups

Adds a fast path for loading DYNAMIC_GLOBAL variables, which are lookup
variables that can be globally loaded, without calling the runtime, as long as
there was no context extension by a sloppy eval along their context chain.

BUG=v8:5263

Review-Url: https://codereview.chromium.org/2347143002
Cr-Commit-Position: refs/heads/master@{#39537}
parent cab644f3
......@@ -915,6 +915,33 @@ void BytecodeGraphBuilder::VisitLdaLookupContextSlotInsideTypeof() {
BuildLdaLookupContextSlot(TypeofMode::INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::BuildLdaLookupGlobalSlot(TypeofMode typeof_mode) {
// TODO(leszeks): Build the fast path here.
// Slow path, do a runtime load lookup.
{
FrameStateBeforeAndAfter states(this);
Node* name =
jsgraph()->Constant(bytecode_iterator().GetConstantForIndexOperand(0));
const Operator* op =
javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
? Runtime::kLoadLookupSlot
: Runtime::kLoadLookupSlotInsideTypeof);
Node* value = NewNode(op, name);
environment()->BindAccumulator(value, &states);
}
}
void BytecodeGraphBuilder::VisitLdaLookupGlobalSlot() {
BuildLdaLookupGlobalSlot(TypeofMode::NOT_INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::VisitLdaLookupGlobalSlotInsideTypeof() {
BuildLdaLookupGlobalSlot(TypeofMode::INSIDE_TYPEOF);
}
void BytecodeGraphBuilder::BuildStaLookupSlot(LanguageMode language_mode) {
FrameStateBeforeAndAfter states(this);
Node* value = environment()->LookupAccumulator();
......
......@@ -132,6 +132,7 @@ class BytecodeGraphBuilder {
void BuildKeyedStore(LanguageMode language_mode);
void BuildLdaLookupSlot(TypeofMode typeof_mode);
void BuildLdaLookupContextSlot(TypeofMode typeof_mode);
void BuildLdaLookupGlobalSlot(TypeofMode typeof_mode);
void BuildStaLookupSlot(LanguageMode language_mode);
void BuildCall(TailCallMode tail_call_mode);
void BuildThrow();
......
......@@ -307,6 +307,18 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupContextSlot(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupGlobalSlot(
const Handle<String> name, TypeofMode typeof_mode, int feedback_slot,
int depth) {
Bytecode bytecode = (typeof_mode == INSIDE_TYPEOF)
? Bytecode::kLdaLookupGlobalSlotInsideTypeof
: Bytecode::kLdaLookupGlobalSlot;
size_t name_index = GetConstantPoolEntry(name);
Output(bytecode, UnsignedOperand(name_index), UnsignedOperand(feedback_slot),
UnsignedOperand(depth));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot(
const Handle<String> name, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode);
......
......@@ -138,6 +138,13 @@ class BytecodeArrayBuilder final : public ZoneObject {
TypeofMode typeof_mode,
int slot_index, int depth);
// Lookup the variable with |name|, which has its feedback in |feedback_slot|
// and is known to be global if not shadowed by a context extension somewhere
// up to |depth| in that context chain.
BytecodeArrayBuilder& LoadLookupGlobalSlot(const Handle<String> name,
TypeofMode typeof_mode,
int feedback_slot, int depth);
// Store value in the accumulator into the variable with |name|.
BytecodeArrayBuilder& StoreLookupSlot(const Handle<String> name,
LanguageMode language_mode);
......
......@@ -1977,6 +1977,12 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
BuildHoleCheckForVariableLoad(variable);
break;
}
case DYNAMIC_GLOBAL: {
int depth = scope()->ContextChainLengthUntilOutermostSloppyEval();
builder()->LoadLookupGlobalSlot(variable->name(), typeof_mode,
feedback_index(slot), depth);
break;
}
default:
builder()->LoadLookupSlot(variable->name(), typeof_mode);
}
......
......@@ -117,9 +117,13 @@ namespace interpreter {
V(LdaLookupSlot, AccumulatorUse::kWrite, OperandType::kIdx) \
V(LdaLookupContextSlot, AccumulatorUse::kWrite, OperandType::kIdx, \
OperandType::kIdx, OperandType::kUImm) \
V(LdaLookupGlobalSlot, AccumulatorUse::kWrite, OperandType::kIdx, \
OperandType::kIdx, OperandType::kUImm) \
V(LdaLookupSlotInsideTypeof, AccumulatorUse::kWrite, OperandType::kIdx) \
V(LdaLookupContextSlotInsideTypeof, AccumulatorUse::kWrite, \
OperandType::kIdx, OperandType::kIdx, OperandType::kUImm) \
V(LdaLookupGlobalSlotInsideTypeof, AccumulatorUse::kWrite, \
OperandType::kIdx, OperandType::kIdx, OperandType::kUImm) \
V(StaLookupSlotSloppy, AccumulatorUse::kReadWrite, OperandType::kIdx) \
V(StaLookupSlotStrict, AccumulatorUse::kReadWrite, OperandType::kIdx) \
\
......
......@@ -421,16 +421,14 @@ void Interpreter::DoMov(InterpreterAssembler* assembler) {
__ Dispatch();
}
Node* Interpreter::BuildLoadGlobal(Callable ic,
Node* Interpreter::BuildLoadGlobal(Callable ic, Node* context,
Node* feedback_slot,
InterpreterAssembler* assembler) {
typedef LoadGlobalWithVectorDescriptor Descriptor;
// Get the global object.
Node* context = __ GetContext();
// Load the global via the LoadGlobalIC.
Node* code_target = __ HeapConstant(ic.code());
Node* raw_slot = __ BytecodeOperandIdx(0);
Node* smi_slot = __ SmiTag(raw_slot);
Node* smi_slot = __ SmiTag(feedback_slot);
Node* type_feedback_vector = __ LoadTypeFeedbackVector();
return __ CallStub(ic.descriptor(), code_target, context,
Arg(Descriptor::kSlot, smi_slot),
......@@ -444,7 +442,11 @@ Node* Interpreter::BuildLoadGlobal(Callable ic,
void Interpreter::DoLdaGlobal(InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::LoadGlobalICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF);
Node* result = BuildLoadGlobal(ic, assembler);
Node* context = __ GetContext();
Node* raw_slot = __ BytecodeOperandIdx(0);
Node* result = BuildLoadGlobal(ic, context, raw_slot, assembler);
__ SetAccumulator(result);
__ Dispatch();
}
......@@ -456,7 +458,11 @@ void Interpreter::DoLdaGlobal(InterpreterAssembler* assembler) {
void Interpreter::DoLdrGlobal(InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::LoadGlobalICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF);
Node* result = BuildLoadGlobal(ic, assembler);
Node* context = __ GetContext();
Node* raw_slot = __ BytecodeOperandIdx(0);
Node* result = BuildLoadGlobal(ic, context, raw_slot, assembler);
Node* destination = __ BytecodeOperandReg(1);
__ StoreRegister(result, destination);
__ Dispatch();
......@@ -469,7 +475,11 @@ void Interpreter::DoLdrGlobal(InterpreterAssembler* assembler) {
void Interpreter::DoLdaGlobalInsideTypeof(InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::LoadGlobalICInOptimizedCode(isolate_, INSIDE_TYPEOF);
Node* result = BuildLoadGlobal(ic, assembler);
Node* context = __ GetContext();
Node* raw_slot = __ BytecodeOperandIdx(0);
Node* result = BuildLoadGlobal(ic, context, raw_slot, assembler);
__ SetAccumulator(result);
__ Dispatch();
}
......@@ -563,8 +573,8 @@ void Interpreter::DoStaContextSlot(InterpreterAssembler* assembler) {
void Interpreter::DoLdaLookupSlot(Runtime::FunctionId function_id,
InterpreterAssembler* assembler) {
Node* index = __ BytecodeOperandIdx(0);
Node* name = __ LoadConstantPoolEntry(index);
Node* name_index = __ BytecodeOperandIdx(0);
Node* name = __ LoadConstantPoolEntry(name_index);
Node* context = __ GetContext();
Node* result = __ CallRuntime(function_id, context, name);
__ SetAccumulator(result);
......@@ -634,6 +644,56 @@ void Interpreter::DoLdaLookupContextSlotInsideTypeof(
DoLdaLookupContextSlot(Runtime::kLoadLookupSlotInsideTypeof, assembler);
}
void Interpreter::DoLdaLookupGlobalSlot(Runtime::FunctionId function_id,
InterpreterAssembler* assembler) {
Node* context = __ GetContext();
Node* name_index = __ BytecodeOperandIdx(0);
Node* feedback_slot = __ BytecodeOperandIdx(1);
Node* depth = __ BytecodeOperandUImm(2);
Label slowpath(assembler, Label::kDeferred);
// Check for context extensions to allow the fast path
__ GotoIfHasContextExtensionUpToDepth(context, depth, &slowpath);
// Fast path does a normal load global
{
Callable ic = CodeFactory::LoadGlobalICInOptimizedCode(
isolate_, function_id == Runtime::kLoadLookupSlotInsideTypeof
? INSIDE_TYPEOF
: NOT_INSIDE_TYPEOF);
Node* result = BuildLoadGlobal(ic, context, feedback_slot, assembler);
__ SetAccumulator(result);
__ Dispatch();
}
// Slow path when we have to call out to the runtime
__ Bind(&slowpath);
{
Node* name = __ LoadConstantPoolEntry(name_index);
Node* result = __ CallRuntime(function_id, context, name);
__ SetAccumulator(result);
__ Dispatch();
}
}
// LdaLookupGlobalSlot <name_index> <feedback_slot> <depth>
//
// Lookup the object with the name in constant pool entry |name_index|
// dynamically.
void Interpreter::DoLdaLookupGlobalSlot(InterpreterAssembler* assembler) {
DoLdaLookupGlobalSlot(Runtime::kLoadLookupSlot, assembler);
}
// LdaLookupGlobalSlotInsideTypeof <name_index> <feedback_slot> <depth>
//
// Lookup the object with the name in constant pool entry |name_index|
// dynamically without causing a NoReferenceError.
void Interpreter::DoLdaLookupGlobalSlotInsideTypeof(
InterpreterAssembler* assembler) {
DoLdaLookupGlobalSlot(Runtime::kLoadLookupSlotInsideTypeof, assembler);
}
void Interpreter::DoStaLookupSlot(LanguageMode language_mode,
InterpreterAssembler* assembler) {
Node* value = __ GetAccumulator();
......
......@@ -137,7 +137,13 @@ class Interpreter {
void DoLdaLookupContextSlot(Runtime::FunctionId function_id,
InterpreterAssembler* assembler);
// Generates code to perform a lookup slot store depending on |language_mode|.
// Generates code to perform a lookup slot load via |function_id| that can
// fast path to a global load.
void DoLdaLookupGlobalSlot(Runtime::FunctionId function_id,
InterpreterAssembler* assembler);
// Generates code to perform a lookup slot store depending on
// |language_mode|.
void DoStaLookupSlot(LanguageMode language_mode,
InterpreterAssembler* assembler);
......@@ -145,7 +151,9 @@ class Interpreter {
compiler::Node* BuildLoadContextSlot(InterpreterAssembler* assembler);
// Generates code to load a global.
compiler::Node* BuildLoadGlobal(Callable ic, InterpreterAssembler* assembler);
compiler::Node* BuildLoadGlobal(Callable ic, compiler::Node* context,
compiler::Node* feedback_slot,
InterpreterAssembler* assembler);
// Generates code to load a named property.
compiler::Node* BuildLoadNamedProperty(Callable ic,
......
......@@ -1117,6 +1117,56 @@ TEST(BytecodeGraphBuilderLookupContextSlot) {
}
}
TEST(BytecodeGraphBuilderLookupGlobalSlot) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
// Testing with eval called in the current context.
const char* inner_eval_prologue = "x = 0; function inner() {";
const char* inner_eval_epilogue = "}; return inner();";
ExpectedSnippet<0> inner_eval_snippets[] = {
{"eval(''); return x;", {factory->NewNumber(0)}},
{"eval('var x = 1'); return x;", {factory->NewNumber(1)}},
{"'use strict'; eval('var x = 1'); return x;", {factory->NewNumber(0)}}};
for (size_t i = 0; i < arraysize(inner_eval_snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s(p1) { %s %s %s } ; %s() ;", kFunctionName,
inner_eval_prologue, inner_eval_snippets[i].code_snippet,
inner_eval_epilogue, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*inner_eval_snippets[i].return_value()));
}
// Testing with eval called in a parent context.
const char* outer_eval_prologue = "";
const char* outer_eval_epilogue =
"function inner() { return x; }; return inner();";
ExpectedSnippet<0> outer_eval_snippets[] = {
{"x = 0; eval('');", {factory->NewNumber(0)}},
{"x = 0; eval('var x = 1');", {factory->NewNumber(1)}},
{"'use strict'; x = 0; eval('var x = 1');", {factory->NewNumber(0)}}};
for (size_t i = 0; i < arraysize(outer_eval_snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s %s %s } ; %s() ;", kFunctionName,
outer_eval_prologue, outer_eval_snippets[i].code_snippet,
outer_eval_epilogue, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*outer_eval_snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderLookupSlotWide) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
......
......@@ -13,7 +13,7 @@ snippet: "
"
frame size: 10
parameter count: 1
bytecode array length: 71
bytecode array length: 73
bytecodes: [
B(CreateFunctionContext), U8(3),
B(PushContext), R(0),
......@@ -41,7 +41,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), U8(6),
B(Star), R(1),
/* 14 E> */ B(Call), R(1), R(2), U8(2), U8(0),
/* 35 S> */ B(LdaLookupSlot), U8(2),
/* 35 S> */ B(LdaLookupGlobalSlot), U8(2), U8(4), U8(1),
/* 45 S> */ B(Return),
]
constant pool: [
......@@ -58,7 +58,7 @@ snippet: "
"
frame size: 10
parameter count: 1
bytecode array length: 72
bytecode array length: 74
bytecodes: [
B(CreateFunctionContext), U8(3),
B(PushContext), R(0),
......@@ -86,7 +86,7 @@ bytecodes: [
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), U8(6),
B(Star), R(1),
/* 14 E> */ B(Call), R(1), R(2), U8(2), U8(0),
/* 35 S> */ B(LdaLookupSlotInsideTypeof), U8(2),
/* 35 S> */ B(LdaLookupGlobalSlotInsideTypeof), U8(2), U8(4), U8(1),
B(TypeOf),
/* 52 S> */ B(Return),
]
......@@ -194,3 +194,53 @@ constant pool: [
handlers: [
]
---
snippet: "
x = 20;
f = function(){
eval('var x = 10');
return x;
}
f();
"
frame size: 10
parameter count: 1
bytecode array length: 73
bytecodes: [
B(CreateFunctionContext), U8(3),
B(PushContext), R(0),
B(Ldar), R(this),
B(StaContextSlot), R(context), U8(4), U8(0),
B(CreateMappedArguments),
B(StaContextSlot), R(context), U8(6), U8(0),
B(Ldar), R(new_target),
B(StaContextSlot), R(context), U8(5), U8(0),
/* 34 E> */ B(StackCheck),
/* 40 S> */ B(LdaConstant), U8(0),
B(Star), R(3),
B(CallRuntimeForPair), U16(Runtime::kLoadLookupSlotForCall), R(3), U8(1), R(1),
B(LdaConstant), U8(1),
B(Star), R(3),
B(LdaZero),
B(Star), R(7),
B(LdaSmi), U8(34),
B(Star), R(8),
B(LdaSmi), U8(40),
B(Star), R(9),
B(Mov), R(1), R(4),
B(Mov), R(3), R(5),
B(Mov), R(closure), R(6),
B(CallRuntime), U16(Runtime::kResolvePossiblyDirectEval), R(4), U8(6),
B(Star), R(1),
/* 40 E> */ B(Call), R(1), R(2), U8(2), U8(0),
/* 62 S> */ B(LdaLookupGlobalSlot), U8(2), U8(4), U8(1),
/* 72 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["eval"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["var x = 10"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["x"],
]
handlers: [
]
......@@ -18,10 +18,10 @@ snippet: "
"
frame size: 0
parameter count: 1
bytecode array length: 4
bytecode array length: 6
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 15 S> */ B(LdaLookupSlot), U8(0),
/* 15 S> */ B(LdaLookupGlobalSlot), U8(0), U8(2), U8(1),
/* 25 S> */ B(Return),
]
constant pool: [
......@@ -91,10 +91,10 @@ snippet: "
"
frame size: 0
parameter count: 1
bytecode array length: 5
bytecode array length: 7
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 15 S> */ B(LdaLookupSlotInsideTypeof), U8(0),
/* 15 S> */ B(LdaLookupGlobalSlotInsideTypeof), U8(0), U8(2), U8(1),
B(TypeOf),
/* 32 S> */ B(Return),
]
......
......@@ -278,7 +278,7 @@ snippet: "
"
frame size: 1
parameter count: 1
bytecode array length: 1030
bytecode array length: 1034
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 22 S> */ B(LdaConstant), U8(0),
......@@ -793,7 +793,7 @@ bytecodes: [
B(Star), R(0),
/* 3082 S> */ B(LdaConstant), U8(255),
B(Star), R(0),
/* 3086 S> */ B(Wide), B(LdaLookupSlot), U16(256),
/* 3086 S> */ B(Wide), B(LdaLookupGlobalSlot), U16(256), U16(2), U16(1),
/* 3095 S> */ B(Return),
]
constant pool: [
......@@ -1329,7 +1329,7 @@ snippet: "
"
frame size: 1
parameter count: 1
bytecode array length: 1031
bytecode array length: 1035
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 22 S> */ B(LdaConstant), U8(0),
......@@ -1844,7 +1844,7 @@ bytecodes: [
B(Star), R(0),
/* 3082 S> */ B(LdaConstant), U8(255),
B(Star), R(0),
/* 3086 S> */ B(Wide), B(LdaLookupSlotInsideTypeof), U16(256),
/* 3086 S> */ B(Wide), B(LdaLookupGlobalSlotInsideTypeof), U16(256), U16(2), U16(1),
B(TypeOf),
/* 3102 S> */ B(Return),
]
......
......@@ -1814,6 +1814,13 @@ TEST(LookupSlot) {
" eval('var x = 10');\n"
" return x;\n"
"}\n"
"f();\n",
"x = 20;\n"
"f = function(){\n"
" eval('var x = 10');\n"
" return x;\n"
"}\n"
"f();\n"
};
// clang-format on
......
......@@ -3906,6 +3906,47 @@ TEST(InterpreterLookupContextSlot) {
}
}
TEST(InterpreterLookupGlobalSlot) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
const char* inner_function_prologue = "function inner() {";
const char* inner_function_epilogue = "};";
const char* outer_function_epilogue = "return inner();";
std::tuple<const char*, const char*, Handle<Object>> lookup_slot[] = {
// Eval in inner context.
std::make_tuple("x = 0;", "eval(''); return x;",
handle(Smi::FromInt(0), isolate)),
std::make_tuple("x = 0;", "eval('var x = 1'); return x;",
handle(Smi::FromInt(1), isolate)),
std::make_tuple("x = 0;", "'use strict'; eval('var x = 1'); return x;",
handle(Smi::FromInt(0), isolate)),
// Eval in outer context.
std::make_tuple("x = 0; eval('');", "return x;",
handle(Smi::FromInt(0), isolate)),
std::make_tuple("x = 0; eval('var x = 1');", "return x;",
handle(Smi::FromInt(1), isolate)),
std::make_tuple("'use strict'; x = 0; eval('var x = 1');", "return x;",
handle(Smi::FromInt(0), isolate)),
};
for (size_t i = 0; i < arraysize(lookup_slot); i++) {
std::string body = std::string(std::get<0>(lookup_slot[i])) +
std::string(inner_function_prologue) +
std::string(std::get<1>(lookup_slot[i])) +
std::string(inner_function_epilogue) +
std::string(outer_function_epilogue);
std::string script = InterpreterTester::SourceForBody(body.c_str());
InterpreterTester tester(isolate, script.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*std::get<2>(lookup_slot[i])));
}
}
TEST(InterpreterCallLookupSlot) {
HandleAndZoneScope handles;
Isolate* isolate = handles.main_isolate();
......
......@@ -104,6 +104,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.LoadLookupContextSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
.LoadLookupContextSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
// Emit load / store lookup slots with global fast paths.
builder.LoadLookupGlobalSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
.LoadLookupGlobalSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
// Emit closure operations.
builder.CreateClosure(0, NOT_TENURED);
......
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