[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:
parent
6f0850c63d
commit
a8e86c49ec
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user