[Interpreter] Add New, CallRuntime and CallJSRuntime support to BytecodeGraphBuilder.

Adds support for the New, CallRuntime and CallJSRuntime bytecodes in
BytecodeGraphBuilder. Also adds BuildLoadObjectField,
BuildLoadGlobalObject and BuildLoadNativeContextField helpers.

Landed on behalf of rmcilroy.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32136}
This commit is contained in:
oth 2015-11-20 01:25:13 -08:00 committed by Commit bot
parent 1e03334e76
commit 8cfa73ac38
8 changed files with 292 additions and 21 deletions

View File

@ -134,6 +134,12 @@ Node* BytecodeGraphBuilder::GetFunctionClosure() {
}
Node* BytecodeGraphBuilder::BuildLoadObjectField(Node* object, int offset) {
return NewNode(jsgraph()->machine()->Load(kMachAnyTagged), object,
jsgraph()->IntPtrConstant(offset - kHeapObjectTag));
}
Node* BytecodeGraphBuilder::BuildLoadImmutableObjectField(Node* object,
int offset) {
return graph()->NewNode(jsgraph()->machine()->Load(kMachAnyTagged), object,
@ -142,6 +148,21 @@ Node* BytecodeGraphBuilder::BuildLoadImmutableObjectField(Node* object,
}
Node* BytecodeGraphBuilder::BuildLoadGlobalObject() {
const Operator* load_op =
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true);
return NewNode(load_op, GetFunctionContext());
}
Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
Node* global = BuildLoadGlobalObject();
Node* native_context =
BuildLoadObjectField(global, JSGlobalObject::kNativeContextOffset);
return NewNode(javascript()->LoadContext(0, index, true), native_context);
}
Node* BytecodeGraphBuilder::BuildLoadFeedbackVector() {
if (!feedback_vector_.is_set()) {
Node* closure = GetFunctionClosure();
@ -650,11 +671,11 @@ void BytecodeGraphBuilder::VisitCreateObjectLiteral(
Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
interpreter::Register callee,
Node* callee,
interpreter::Register receiver,
size_t arity) {
Node** all = info()->zone()->NewArray<Node*>(arity);
all[0] = environment()->LookupRegister(callee);
Node** all = info()->zone()->NewArray<Node*>(static_cast<int>(arity));
all[0] = callee;
all[1] = environment()->LookupRegister(receiver);
int receiver_index = receiver.index();
for (int i = 2; i < static_cast<int>(arity); ++i) {
@ -672,7 +693,7 @@ void BytecodeGraphBuilder::BuildCall(
// register has been loaded with null / undefined explicitly or we are sure it
// is not null / undefined.
ConvertReceiverMode receiver_hint = ConvertReceiverMode::kAny;
interpreter::Register callee = iterator.GetRegisterOperand(0);
Node* callee = environment()->LookupRegister(iterator.GetRegisterOperand(0));
interpreter::Register receiver = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(3));
@ -697,21 +718,78 @@ void BytecodeGraphBuilder::VisitCallWide(
}
void BytecodeGraphBuilder::VisitCallRuntime(
void BytecodeGraphBuilder::VisitCallJSRuntime(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Node* callee = BuildLoadNativeContextField(iterator.GetIndexOperand(0));
interpreter::Register receiver = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
// Create node to perform the JS runtime call.
const Operator* call =
javascript()->CallFunction(arg_count + 2, language_mode());
Node* value = ProcessCallArguments(call, callee, receiver, arg_count + 2);
AddEmptyFrameStateInputs(value);
environment()->BindAccumulator(value);
}
void BytecodeGraphBuilder::VisitCallJSRuntime(
Node* BytecodeGraphBuilder::ProcessCallRuntimeArguments(
const Operator* call_runtime_op, interpreter::Register first_arg,
size_t arity) {
Node** all = info()->zone()->NewArray<Node*>(arity);
int first_arg_index = first_arg.index();
for (int i = 0; i < static_cast<int>(arity); ++i) {
all[i] = environment()->LookupRegister(
interpreter::Register(first_arg_index + i));
}
Node* value = MakeNode(call_runtime_op, static_cast<int>(arity), all, false);
return value;
}
void BytecodeGraphBuilder::VisitCallRuntime(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Runtime::FunctionId functionId =
static_cast<Runtime::FunctionId>(iterator.GetIndexOperand(0));
interpreter::Register first_arg = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
// Create node to perform the runtime call.
const Operator* call = javascript()->CallRuntime(functionId, arg_count);
Node* value = ProcessCallRuntimeArguments(call, first_arg, arg_count);
AddEmptyFrameStateInputs(value);
environment()->BindAccumulator(value);
}
Node* BytecodeGraphBuilder::ProcessCallNewArguments(
const Operator* call_new_op, interpreter::Register callee,
interpreter::Register first_arg, size_t arity) {
Node** all = info()->zone()->NewArray<Node*>(arity);
all[0] = environment()->LookupRegister(callee);
int first_arg_index = first_arg.index();
for (int i = 1; i < static_cast<int>(arity) - 1; ++i) {
all[i] = environment()->LookupRegister(
interpreter::Register(first_arg_index + i - 1));
}
// Original constructor is the same as the callee.
all[arity - 1] = environment()->LookupRegister(callee);
Node* value = MakeNode(call_new_op, static_cast<int>(arity), all, false);
return value;
}
void BytecodeGraphBuilder::VisitNew(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
interpreter::Register callee = iterator.GetRegisterOperand(0);
interpreter::Register first_arg = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
const Operator* call =
javascript()->CallConstruct(static_cast<int>(arg_count) + 2);
Node* value = ProcessCallNewArguments(call, callee, first_arg, arg_count + 2);
AddEmptyFrameStateInputs(value);
environment()->BindAccumulator(value);
}

View File

@ -40,12 +40,19 @@ class BytecodeGraphBuilder {
// Get or create the node that represents the outer function context.
Node* GetFunctionContext();
// Builders for accessing an immutable object field.
// Builder for accessing a (potentially immutable) object field.
Node* BuildLoadObjectField(Node* object, int offset);
Node* BuildLoadImmutableObjectField(Node* object, int offset);
// Builder for accessing type feedback vector.
Node* BuildLoadFeedbackVector();
// Builder for loading the global object.
Node* BuildLoadGlobalObject();
// Builder for loading the a native context field.
Node* BuildLoadNativeContextField(int index);
// Helper function for creating a pair containing type feedback vector and
// a feedback slot.
VectorSlotPair CreateVectorSlotPair(int slot_id);
@ -91,9 +98,14 @@ class BytecodeGraphBuilder {
void UpdateControlDependencyToLeaveFunction(Node* exit);
Node* ProcessCallArguments(const Operator* call_op,
interpreter::Register callee,
Node* ProcessCallArguments(const Operator* call_op, Node* callee,
interpreter::Register receiver, size_t arity);
Node* ProcessCallNewArguments(const Operator* call_new_op,
interpreter::Register callee,
interpreter::Register first_arg, size_t arity);
Node* ProcessCallRuntimeArguments(const Operator* call_runtime_op,
interpreter::Register first_arg,
size_t arity);
void BuildLoadGlobal(const interpreter::BytecodeArrayIterator& iterator,
TypeofMode typeof_mode);

View File

@ -953,7 +953,7 @@ void Interpreter::DoCallJSRuntime(compiler::InterpreterAssembler* assembler) {
}
// New <constructor> <arg_count>
// New <constructor> <first_arg> <arg_count>
//
// Call operator new with |constructor| and the first argument in
// register |first_arg| and |arg_count| arguments in subsequent

View File

@ -64,6 +64,7 @@ class BytecodeGraphTester {
: isolate_(isolate), zone_(zone), script_(script) {
i::FLAG_ignition = true;
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
// Set ignition filter flag via SetFlagsFromString to avoid double-free
// (or potential leak with StrDup() based on ownership confusion).
ScopedVector<char> ignition_filter(64);
@ -535,6 +536,74 @@ TEST(BytecodeGraphBuilderPropertyCall) {
}
TEST(BytecodeGraphBuilderCallNew) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<0> snippets[] = {
{"function counter() { this.count = 20; }\n"
"function f() {\n"
" var c = new counter();\n"
" return c.count;\n"
"}; f()",
{factory->NewNumberFromInt(20)}},
{"function counter(arg0) { this.count = 17; this.x = arg0; }\n"
"function f() {\n"
" var c = new counter(6);\n"
" return c.count + c.x;\n"
"}; f()",
{factory->NewNumberFromInt(23)}},
{"function counter(arg0, arg1) {\n"
" this.count = 17; this.x = arg0; this.y = arg1;\n"
"}\n"
"function f() {\n"
" var c = new counter(3, 5);\n"
" return c.count + c.x + c.y;\n"
"}; f()",
{factory->NewNumberFromInt(25)}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderCallRuntime) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"function f(arg0) { return %MaxSmi(); }\nf()",
{factory->NewNumberFromInt(Smi::kMaxValue), factory->undefined_value()}},
{"function f(arg0) { return %IsArray(arg0) }\nf(undefined)",
{factory->true_value(), BytecodeGraphTester::NewObject("[1, 2, 3]")}},
{"function f(arg0) { return %Add(arg0, 2) }\nf(1)",
{factory->NewNumberFromInt(5), factory->NewNumberFromInt(3)}},
{"function f(arg0) { return %spread_arguments(arg0).length }\nf([])",
{factory->NewNumberFromInt(3),
BytecodeGraphTester::NewObject("[1, 2, 3]")}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
BytecodeGraphTester tester(isolate, zone, snippets[i].code_snippet);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_value =
callable(snippets[i].parameter(0)).ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
TEST(BytecodeGraphBuilderGlobals) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();

View File

@ -400,7 +400,6 @@ TEST(LogicalExpressions) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{"var x = 0; return x || 3;",
1 * kPointerSize,

View File

@ -721,6 +721,92 @@ TEST_F(BytecodeGraphBuilderTest, KeyedStore) {
}
}
TEST_F(BytecodeGraphBuilderTest, CallRuntime) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(2);
array_builder.set_context_count(0);
array_builder.set_parameter_count(3);
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.StoreAccumulatorInRegister(interpreter::Register(0))
.LoadAccumulatorWithRegister(array_builder.Parameter(2))
.StoreAccumulatorInRegister(interpreter::Register(1))
.CallRuntime(Runtime::kAdd, interpreter::Register(0), 2)
.Return();
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
Node* start = graph->start();
Node* ret = graph->end()->InputAt(0);
std::vector<Matcher<Node*>> call_inputs;
call_inputs.push_back(IsParameter(1));
call_inputs.push_back(IsParameter(2));
Matcher<Node*> call_js_runtime = IsJSCallRuntime(call_inputs, start, start);
EXPECT_THAT(ret, IsReturn(call_js_runtime, call_js_runtime, IsIfSuccess(_)));
}
TEST_F(BytecodeGraphBuilderTest, CallJSRuntime) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(1);
array_builder.set_context_count(1);
array_builder.set_parameter_count(2);
// function f(arg) { return %spread_arguments(arg0); }
interpreter::Register reg0 = interpreter::Register(0);
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.StoreAccumulatorInRegister(reg0)
.CallJSRuntime(Context::SPREAD_ARGUMENTS_INDEX, reg0, 1)
.Return();
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
Node* ret = graph->end()->InputAt(0);
Matcher<Node*> load_context =
IsLoadContext(ContextAccess(0, Context::SPREAD_ARGUMENTS_INDEX, true), _);
std::vector<Matcher<Node*>> call_inputs;
call_inputs.push_back(load_context);
call_inputs.push_back(IsParameter(1));
Matcher<Node*> call_js_function =
IsJSCallFunction(call_inputs, load_context, graph->start());
EXPECT_THAT(ret,
IsReturn(call_js_function, call_js_function, IsIfSuccess(_)));
}
TEST_F(BytecodeGraphBuilderTest, New) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(4);
array_builder.set_context_count(0);
array_builder.set_parameter_count(5);
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.StoreAccumulatorInRegister(interpreter::Register(0))
.LoadAccumulatorWithRegister(array_builder.Parameter(2))
.StoreAccumulatorInRegister(interpreter::Register(1))
.LoadAccumulatorWithRegister(array_builder.Parameter(3))
.StoreAccumulatorInRegister(interpreter::Register(2))
.LoadAccumulatorWithRegister(array_builder.Parameter(4))
.StoreAccumulatorInRegister(interpreter::Register(3))
.New(interpreter::Register(0), interpreter::Register(1), 3)
.Return();
auto bytecode_array = array_builder.ToBytecodeArray();
Graph* graph = GetCompletedGraph(bytecode_array);
Node* start = graph->start();
Node* ret = graph->end()->InputAt(0);
std::vector<Matcher<Node*>> construct_inputs;
construct_inputs.push_back(IsParameter(1));
construct_inputs.push_back(IsParameter(2));
construct_inputs.push_back(IsParameter(3));
construct_inputs.push_back(IsParameter(4));
construct_inputs.push_back(IsParameter(1));
Matcher<Node*> call_construct =
IsJSCallConstruct(construct_inputs, start, start);
EXPECT_THAT(ret, IsReturn(call_construct, call_construct, IsIfSuccess(_)));
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -1805,12 +1805,13 @@ class IsJSStorePropertyMatcher final : public NodeMatcher {
};
class IsJSCallFunctionMatcher final : public NodeMatcher {
class IsJSCallMatcher final : public NodeMatcher {
public:
IsJSCallFunctionMatcher(const std::vector<Matcher<Node*>>& value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kJSCallFunction),
IsJSCallMatcher(IrOpcode::Value op_code,
const std::vector<Matcher<Node*>>& value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(op_code),
value_matchers_(value_matchers),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
@ -2472,11 +2473,21 @@ Matcher<Node*> IsJSStoreGlobal(const Handle<Name> name,
}
Matcher<Node*> IsJSCallConstruct(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSCallMatcher(IrOpcode::kJSCallConstruct,
value_matchers, effect_matcher,
control_matcher));
}
Matcher<Node*> IsJSCallFunction(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSCallFunctionMatcher(value_matchers, effect_matcher,
control_matcher));
return MakeMatcher(new IsJSCallMatcher(IrOpcode::kJSCallFunction,
value_matchers, effect_matcher,
control_matcher));
}
@ -2514,6 +2525,16 @@ Matcher<Node*> IsJSStoreProperty(const Matcher<Node*>& object_matcher,
effect_matcher, control_matcher));
}
Matcher<Node*> IsJSCallRuntime(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSCallMatcher(IrOpcode::kJSCallRuntime,
value_matchers, effect_matcher,
control_matcher));
}
#define IS_BINOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& rhs_matcher) { \

View File

@ -390,9 +390,15 @@ Matcher<Node*> IsJSStoreProperty(const Matcher<Node*>& object_matcher,
const Matcher<Node*>& feedback_vector_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsJSCallConstruct(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsJSCallFunction(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsJSCallRuntime(std::vector<Matcher<Node*>> value_matchers,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsJSUnaryNot(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsJSTypeOf(const Matcher<Node*>& value_matcher);
Matcher<Node*> IsJSDeleteProperty(const Matcher<Node*>& object_value_matcher,