[Interpreter] Add support for context slot loads / stores to BytecodeGraphBuilder.

Adds implementation and tests for LdaContextSlot, StaeContextSlot, PushContext,
and PopContext to bytecode graph builder

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32474}
This commit is contained in:
mythria 2015-12-01 09:28:43 -08:00 committed by Commit bot
parent df36d046a4
commit 128e75cbd9
4 changed files with 194 additions and 19 deletions

View File

@ -7,6 +7,7 @@
#include "src/compiler/linkage.h"
#include "src/compiler/operator-properties.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
@ -73,8 +74,16 @@ void BytecodeGraphBuilder::Environment::BindRegister(
Node* BytecodeGraphBuilder::Environment::LookupRegister(
interpreter::Register the_register) const {
int values_index = RegisterToValuesIndex(the_register);
return values()->at(values_index);
if (the_register.is_function_context()) {
return builder()->GetFunctionContext();
} else if (the_register.is_function_closure()) {
return builder()->GetFunctionClosure();
} else if (the_register.is_new_target()) {
return builder()->GetNewTarget();
} else {
int values_index = RegisterToValuesIndex(the_register);
return values()->at(values_index);
}
}
@ -111,6 +120,18 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone,
}
Node* BytecodeGraphBuilder::GetNewTarget() {
if (!new_target_.is_set()) {
int params = bytecode_array()->parameter_count();
int index = Linkage::GetJSCallNewTargetParamIndex(params);
const Operator* op = common()->Parameter(index, "%new.target");
Node* node = NewNode(op, graph()->start());
new_target_.set(node);
}
return new_target_.get();
}
Node* BytecodeGraphBuilder::GetFunctionContext() {
if (!function_context_.is_set()) {
int params = bytecode_array()->parameter_count();
@ -204,13 +225,7 @@ bool BytecodeGraphBuilder::CreateGraph(bool stack_check) {
GetFunctionContext());
set_environment(&env);
// Build function context only if there are context allocated variables.
if (info()->num_heap_slots() > 0) {
UNIMPLEMENTED(); // TODO(oth): Write ast-graph-builder equivalent.
} else {
// Simply use the outer function context in building the graph.
CreateGraphBody(stack_check);
}
CreateGraphBody(stack_check);
// Finish the basic structure of the graph.
DCHECK_NE(0u, exit_controls_.size());
@ -443,13 +458,32 @@ void BytecodeGraphBuilder::VisitStaGlobalStrictWide(
void BytecodeGraphBuilder::VisitLdaContextSlot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
// TODO(mythria): LoadContextSlots are unrolled by the required depth when
// generating bytecode. Hence the value of depth is always 0. Update this
// code, when the implementation changes.
// TODO(mythria): immutable flag is also set to false. This information is not
// available in bytecode array. update this code when the implementation
// changes.
const Operator* op =
javascript()->LoadContext(0, iterator.GetIndexOperand(1), false);
Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0));
Node* node = NewNode(op, context);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitStaContextSlot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
// TODO(mythria): LoadContextSlots are unrolled by the required depth when
// generating bytecode. Hence the value of depth is always 0. Update this
// code, when the implementation changes.
const Operator* op =
javascript()->StoreContext(0, iterator.GetIndexOperand(1));
Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0));
Node* value = environment()->LookupAccumulator();
Node* node = NewNode(op, context, value);
CHECK(node != nullptr);
environment()->BindAccumulator(value);
}
@ -624,13 +658,16 @@ void BytecodeGraphBuilder::VisitKeyedStoreICStrictWide(
void BytecodeGraphBuilder::VisitPushContext(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Node* context = environment()->LookupAccumulator();
environment()->BindRegister(iterator.GetRegisterOperand(0), context);
environment()->SetContext(context);
}
void BytecodeGraphBuilder::VisitPopContext(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Node* context = environment()->LookupRegister(iterator.GetRegisterOperand(0));
environment()->SetContext(context);
}

View File

@ -40,6 +40,9 @@ class BytecodeGraphBuilder {
// Get or create the node that represents the outer function context.
Node* GetFunctionContext();
// Get or create the node that represents the incoming new target value.
Node* GetNewTarget();
// Builder for accessing a (potentially immutable) object field.
Node* BuildLoadObjectField(Node* object, int offset);
Node* BuildLoadImmutableObjectField(Node* object, int offset);
@ -158,6 +161,7 @@ class BytecodeGraphBuilder {
// Nodes representing values in the activation record.
SetOncePointer<Node> function_context_;
SetOncePointer<Node> function_closure_;
SetOncePointer<Node> new_target_;
// Optimization to cache loaded feedback vector.
SetOncePointer<Node> feedback_vector_;
@ -199,6 +203,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
}
Node* Context() const { return context_; }
void SetContext(Node* new_context) { context_ = new_context; }
private:
int RegisterToValuesIndex(interpreter::Register the_register) const;

View File

@ -1328,6 +1328,8 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
// Walk the context chain to find the context at the given depth.
// TODO(rmcilroy): Perform this work in a bytecode handler once we have
// a generic mechanism for performing jumps in interpreter.cc.
// TODO(mythria): Also update bytecode graph builder with correct depth
// when this changes.
builder()
->LoadAccumulatorWithRegister(execution_context()->reg())
.StoreAccumulatorInRegister(context_reg);
@ -1404,6 +1406,8 @@ void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
// Walk the context chain to find the context at the given depth.
// TODO(rmcilroy): Perform this work in a bytecode handler once we have
// a generic mechanism for performing jumps in interpreter.cc.
// TODO(mythria): Also update bytecode graph builder with correct depth
// when this changes.
builder()
->StoreAccumulatorInRegister(value_temp)
.LoadAccumulatorWithRegister(execution_context()->reg())

View File

@ -67,7 +67,8 @@ class BytecodeGraphCallable {
class BytecodeGraphTester {
public:
BytecodeGraphTester(Isolate* isolate, Zone* zone, const char* script)
BytecodeGraphTester(Isolate* isolate, Zone* zone, const char* script,
const char* filter = kFunctionName)
: isolate_(isolate), zone_(zone), script_(script) {
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
@ -75,7 +76,7 @@ class BytecodeGraphTester {
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
SNPrintF(ignition_filter, "--ignition-filter=%s", kFunctionName);
SNPrintF(ignition_filter, "--ignition-filter=%s", filter);
FlagList::SetFlagsFromString(ignition_filter.start(),
ignition_filter.length());
// Ensure handler table is generated.
@ -84,8 +85,9 @@ class BytecodeGraphTester {
virtual ~BytecodeGraphTester() {}
template <class... A>
BytecodeGraphCallable<A...> GetCallable() {
return BytecodeGraphCallable<A...>(isolate_, GetFunction());
BytecodeGraphCallable<A...> GetCallable(
const char* functionName = kFunctionName) {
return BytecodeGraphCallable<A...>(isolate_, GetFunction(functionName));
}
Local<Message> CheckThrowsReturnMessage() {
@ -109,11 +111,11 @@ class BytecodeGraphTester {
Zone* zone_;
const char* script_;
Handle<JSFunction> GetFunction() {
Handle<JSFunction> GetFunction(const char* functionName) {
CompileRun(script_);
Local<Function> api_function = Local<Function>::Cast(
CcTest::global()
->Get(CcTest::isolate()->GetCurrentContext(), v8_str(kFunctionName))
->Get(CcTest::isolate()->GetCurrentContext(), v8_str(functionName))
.ToLocalChecked());
Handle<JSFunction> function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
@ -1002,6 +1004,133 @@ TEST(BytecodeGraphBuilderThrow) {
}
}
TEST(BytecodeGraphBuilderContext) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<0> snippets[] = {
{"var x = 'outer';"
"function f() {"
" 'use strict';"
" {"
" let x = 'inner';"
" (function() {x});"
" }"
"return(x);"
"}"
"f();",
{factory->NewStringFromStaticChars("outer")}},
{"var x = 'outer';"
"function f() {"
" 'use strict';"
" {"
" let x = 'inner ';"
" var innerFunc = function() {return x};"
" }"
"return(innerFunc() + x);"
"}"
"f();",
{factory->NewStringFromStaticChars("inner outer")}},
{"var x = 'outer';"
"function f() {"
" 'use strict';"
" {"
" let x = 'inner ';"
" var innerFunc = function() {return x;};"
" {"
" let x = 'innermost ';"
" var innerMostFunc = function() {return x + innerFunc();};"
" }"
" x = 'inner_changed ';"
" }"
" return(innerMostFunc() + x);"
"}"
"f();",
{factory->NewStringFromStaticChars("innermost inner_changed outer")}},
};
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", snippets[i].code_snippet);
BytecodeGraphTester tester(isolate, zone, script.start(), "f");
auto callable = tester.GetCallable<>("f");
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderLoadContext) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"function Outer() {"
" var outerVar = 2;"
" function Inner(innerArg) {"
" this.innerFunc = function () {"
" return outerVar * innerArg;"
" };"
" };"
" this.getInnerFunc = function GetInner() {"
" return new Inner(3).innerFunc;"
" }"
"}"
"var f = new Outer().getInnerFunc();"
"f();",
{factory->NewNumberFromInt(6), factory->undefined_value()}},
{"function Outer() {"
" var outerVar = 2;"
" function Inner(innerArg) {"
" this.innerFunc = function () {"
" outerVar = innerArg; return outerVar;"
" };"
" };"
" this.getInnerFunc = function GetInner() {"
" return new Inner(10).innerFunc;"
" }"
"}"
"var f = new Outer().getInnerFunc();"
"f();",
{factory->NewNumberFromInt(10), factory->undefined_value()}},
{"function testOuter(outerArg) {"
" this.testinnerFunc = function testInner(innerArg) {"
" return innerArg + outerArg;"
" }"
"}"
"var f = new testOuter(10).testinnerFunc;"
"f(0);",
{factory->NewNumberFromInt(14), factory->NewNumberFromInt(4)}},
{"function testOuter(outerArg) {"
" var outerVar = outerArg * 2;"
" this.testinnerFunc = function testInner(innerArg) {"
" outerVar = outerVar + innerArg; return outerVar;"
" }"
"}"
"var f = new testOuter(10).testinnerFunc;"
"f(0);",
{factory->NewNumberFromInt(24), factory->NewNumberFromInt(4)}}};
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", snippets[i].code_snippet);
BytecodeGraphTester tester(isolate, zone, script.start(), "*");
auto callable = tester.GetCallable<Handle<Object>>("f");
Handle<Object> return_value =
callable(snippets[i].parameter(0)).ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8