[Interpreter] Add support for unary operators to bytecode graph builder.

Adds implementation and tests for LogicalNot, TypeOf and Delete operators
to bytecode graph builder.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32104}
This commit is contained in:
mythria 2015-11-19 01:38:32 -08:00 committed by Commit bot
parent 6f0850c63d
commit a8e86c49ec
6 changed files with 262 additions and 4 deletions

View File

@ -757,25 +757,43 @@ void BytecodeGraphBuilder::VisitDec(
void BytecodeGraphBuilder::VisitLogicalNot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Node* node =
NewNode(javascript()->UnaryNot(), environment()->LookupAccumulator());
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitTypeOf(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
Node* node =
NewNode(javascript()->TypeOf(), environment()->LookupAccumulator());
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::BuildDelete(
const interpreter::BytecodeArrayIterator& iterator) {
Node* key = environment()->LookupAccumulator();
Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0));
Node* node =
NewNode(javascript()->DeleteProperty(language_mode()), object, key);
AddEmptyFrameStateInputs(node);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitDeletePropertyStrict(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_strict(language_mode()));
BuildDelete(iterator);
}
void BytecodeGraphBuilder::VisitDeletePropertySloppy(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_sloppy(language_mode()));
BuildDelete(iterator);
}

View File

@ -97,6 +97,7 @@ class BytecodeGraphBuilder {
void BuildCall(const interpreter::BytecodeArrayIterator& iterator);
void BuildBinaryOp(const Operator* op,
const interpreter::BytecodeArrayIterator& iterator);
void BuildDelete(const interpreter::BytecodeArrayIterator& iterator);
// Growth increment for the temporary buffer used to construct input lists to
// new nodes.

View File

@ -414,6 +414,119 @@ TEST(BytecodeGraphBuilderGlobals) {
}
}
TEST(BytecodeGraphBuilderLogicalNot) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"return !p1;",
{factory->false_value(),
BytecodeGraphTester::NewObject("({val : 10})")}},
{"return !p1;", {factory->true_value(), factory->NewNumberFromInt(0)}},
{"return !p1;", {factory->true_value(), factory->undefined_value()}},
{"return !p1;", {factory->false_value(), factory->NewNumberFromInt(10)}},
{"return !p1;", {factory->false_value(), factory->true_value()}},
{"return !p1;",
{factory->false_value(), factory->NewStringFromStaticChars("abc")}},
};
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(p1) { %s }\n%s({});", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
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(BytecodeGraphBuilderTypeOf) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"return typeof p1;",
{factory->NewStringFromStaticChars("object"),
BytecodeGraphTester::NewObject("({val : 10})")}},
{"return typeof p1;",
{factory->NewStringFromStaticChars("undefined"),
factory->undefined_value()}},
{"return typeof p1;",
{factory->NewStringFromStaticChars("number"),
factory->NewNumberFromInt(10)}},
{"return typeof p1;",
{factory->NewStringFromStaticChars("boolean"), factory->true_value()}},
{"return typeof p1;",
{factory->NewStringFromStaticChars("string"),
factory->NewStringFromStaticChars("abc")}},
};
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(p1) { %s }\n%s({});", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
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(BytecodeGraphBuilderDelete) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"return delete p1.val;",
{factory->true_value(), BytecodeGraphTester::NewObject("({val : 10})")}},
{"delete p1.val; return p1.val;",
{factory->undefined_value(),
BytecodeGraphTester::NewObject("({val : 10})")}},
{"delete p1.name; return p1.val;",
{factory->NewNumberFromInt(10),
BytecodeGraphTester::NewObject("({val : 10, name:'abc'})")}},
{"'use strict'; return delete p1.val;",
{factory->true_value(), BytecodeGraphTester::NewObject("({val : 10})")}},
{"'use strict'; delete p1.val; return p1.val;",
{factory->undefined_value(),
BytecodeGraphTester::NewObject("({val : 10})")}},
{"'use strict'; delete p1.name; return p1.val;",
{factory->NewNumberFromInt(10),
BytecodeGraphTester::NewObject("({val : 10, name:'abc'})")}},
// TODO(mythria): Add tests for global and unallocated when we have
// support for LdaContextSlot
};
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(p1) { %s }\n%s({});", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> return_value =
callable(snippets[i].parameter(0)).ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -527,6 +527,70 @@ TEST_F(BytecodeGraphBuilderTest, StoreGlobal) {
}
TEST_F(BytecodeGraphBuilderTest, LogicalNot) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(1);
array_builder.set_context_count(0);
array_builder.set_parameter_count(2);
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.LogicalNot()
.Return();
FeedbackVectorSpec feedback_spec(zone());
Handle<TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate(), &feedback_spec);
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
Node* ret = graph->end()->InputAt(0);
EXPECT_THAT(ret, IsReturn(IsJSUnaryNot(IsParameter(1)), _, _));
}
TEST_F(BytecodeGraphBuilderTest, TypeOf) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(1);
array_builder.set_context_count(0);
array_builder.set_parameter_count(2);
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
.TypeOf()
.Return();
FeedbackVectorSpec feedback_spec(zone());
Handle<TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate(), &feedback_spec);
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
Node* ret = graph->end()->InputAt(0);
EXPECT_THAT(ret, IsReturn(IsJSTypeOf(IsParameter(1)), _, _));
}
TEST_F(BytecodeGraphBuilderTest, Delete) {
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
array_builder.set_locals_count(1);
array_builder.set_context_count(0);
array_builder.set_parameter_count(2);
Handle<Name> name = GetName(isolate(), "val");
array_builder.LoadLiteral(name)
.Delete(array_builder.Parameter(1), language_mode)
.Return();
FeedbackVectorSpec feedback_spec(zone());
Handle<TypeFeedbackVector> vector =
NewTypeFeedbackVector(isolate(), &feedback_spec);
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
language_mode);
Node* start = graph->start();
Node* ret = graph->end()->InputAt(0);
Matcher<Node*> delete_matcher =
IsJSDeleteProperty(IsParameter(1), IsHeapConstant(name), start, start);
EXPECT_THAT(ret, IsReturn(delete_matcher, _, _));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -1428,6 +1428,51 @@ class IsParameterMatcher final : public NodeMatcher {
};
class IsJSDeletePropertyMatcher final : public NodeMatcher {
public:
IsJSDeletePropertyMatcher(const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& key_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kJSDeleteProperty),
object_value_matcher_(object_value_matcher),
key_matcher_(key_matcher),
effect_matcher_(effect_matcher),
control_matcher_(control_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
*os << " whose object (";
object_value_matcher_.DescribeTo(os);
*os << "), key (";
key_matcher_.DescribeTo(os);
*os << "), effect (";
effect_matcher_.DescribeTo(os);
*os << "), and control (";
control_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"object", object_value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), "key",
key_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<Node*> object_value_matcher_;
const Matcher<Node*> key_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
// TODO(mythria): Check if we can use the same matcher for Load and Store
class IsJSLoadNamedMatcher final : public NodeMatcher {
public:
@ -2215,6 +2260,15 @@ Matcher<Node*> IsLoadFramePointer() {
}
Matcher<Node*> IsJSDeleteProperty(const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& key_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSDeletePropertyMatcher(
object_value_matcher, key_matcher, effect_matcher, control_matcher));
}
Matcher<Node*> IsJSLoadNamed(const Handle<Name> name,
const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& feedback_vector_matcher,
@ -2334,6 +2388,8 @@ IS_UNOP_MATCHER(NumberToInt32)
IS_UNOP_MATCHER(NumberToUint32)
IS_UNOP_MATCHER(ObjectIsSmi)
IS_UNOP_MATCHER(Word32Clz)
IS_UNOP_MATCHER(JSUnaryNot)
IS_UNOP_MATCHER(JSTypeOf)
#undef IS_UNOP_MATCHER
} // namespace compiler

View File

@ -376,6 +376,12 @@ Matcher<Node*> IsJSStoreGlobal(const Handle<Name> name,
Matcher<Node*> IsJSCallFunction(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,
const Matcher<Node*>& key_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
} // namespace compiler
} // namespace internal