Commit 2acc2bc2 authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Adds implementation of bytecode graph builder for LoadICSloppy/Strict.

Adds implementation and tests for following operators in bytecode graph builder:
-VisitLoadICSloppy
-VisitLoadICStrict
-VisitLoadICSloppyWide
-VisitLoadICStrictWide

The current implementation introduces empty frame states for frame state inputs expected by these operations.

BUG=v8:4280
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32026}
parent a77f9179
...@@ -123,6 +123,55 @@ Node* BytecodeGraphBuilder::GetFunctionContext() { ...@@ -123,6 +123,55 @@ Node* BytecodeGraphBuilder::GetFunctionContext() {
} }
Node* BytecodeGraphBuilder::GetFunctionClosure() {
if (!function_closure_.is_set()) {
const Operator* op = common()->Parameter(
Linkage::kJSFunctionCallClosureParamIndex, "%closure");
Node* node = NewNode(op, graph()->start());
function_closure_.set(node);
}
return function_closure_.get();
}
Node* BytecodeGraphBuilder::BuildLoadImmutableObjectField(Node* object,
int offset) {
return graph()->NewNode(jsgraph()->machine()->Load(kMachAnyTagged), object,
jsgraph()->IntPtrConstant(offset - kHeapObjectTag),
graph()->start(), graph()->start());
}
Node* BytecodeGraphBuilder::BuildLoadFeedbackVector() {
if (!feedback_vector_.is_set()) {
Node* closure = GetFunctionClosure();
Node* shared = BuildLoadImmutableObjectField(
closure, JSFunction::kSharedFunctionInfoOffset);
Node* vector = BuildLoadImmutableObjectField(
shared, SharedFunctionInfo::kFeedbackVectorOffset);
feedback_vector_.set(vector);
}
return feedback_vector_.get();
}
VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) {
Handle<TypeFeedbackVector> feedback_vector = info()->feedback_vector();
FeedbackVectorSlot slot = feedback_vector->ToSlot(slot_id);
return VectorSlotPair(feedback_vector, slot);
}
void BytecodeGraphBuilder::AddEmptyFrameStateInputs(Node* node) {
int frame_state_count =
OperatorProperties::GetFrameStateInputCount(node->op());
for (int i = 0; i < frame_state_count; i++) {
NodeProperties::ReplaceFrameStateInput(node, i,
jsgraph()->EmptyFrameState());
}
}
bool BytecodeGraphBuilder::CreateGraph(bool stack_check) { bool BytecodeGraphBuilder::CreateGraph(bool stack_check) {
// Set up the basic structure of the graph. Outputs for {Start} are // Set up the basic structure of the graph. Outputs for {Start} are
// the formal parameters (including the receiver) plus context and // the formal parameters (including the receiver) plus context and
...@@ -339,15 +388,33 @@ void BytecodeGraphBuilder::VisitStaContextSlot( ...@@ -339,15 +388,33 @@ void BytecodeGraphBuilder::VisitStaContextSlot(
} }
void BytecodeGraphBuilder::BuildNamedLoad(
const interpreter::BytecodeArrayIterator& iterator) {
Node* object = environment()->LookupRegister(iterator.GetRegisterOperand(0));
Handle<Name> name =
Handle<Name>::cast(iterator.GetConstantForIndexOperand(1));
VectorSlotPair feedback = CreateVectorSlotPair(iterator.GetIndexOperand(2));
const Operator* op = javascript()->LoadNamed(language_mode(), name, feedback);
Node* node = NewNode(op, object, BuildLoadFeedbackVector());
// TODO(mythria): Replace with real frame state. Also add before and after
// frame states if required.
AddEmptyFrameStateInputs(node);
environment()->BindAccumulator(node);
}
void BytecodeGraphBuilder::VisitLoadICSloppy( void BytecodeGraphBuilder::VisitLoadICSloppy(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); DCHECK(is_sloppy(language_mode()));
BuildNamedLoad(iterator);
} }
void BytecodeGraphBuilder::VisitLoadICStrict( void BytecodeGraphBuilder::VisitLoadICStrict(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); DCHECK(is_strict(language_mode()));
BuildNamedLoad(iterator);
} }
...@@ -365,13 +432,15 @@ void BytecodeGraphBuilder::VisitKeyedLoadICStrict( ...@@ -365,13 +432,15 @@ void BytecodeGraphBuilder::VisitKeyedLoadICStrict(
void BytecodeGraphBuilder::VisitLoadICSloppyWide( void BytecodeGraphBuilder::VisitLoadICSloppyWide(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); DCHECK(is_sloppy(language_mode()));
BuildNamedLoad(iterator);
} }
void BytecodeGraphBuilder::VisitLoadICStrictWide( void BytecodeGraphBuilder::VisitLoadICStrictWide(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); DCHECK(is_strict(language_mode()));
BuildNamedLoad(iterator);
} }
...@@ -520,12 +589,7 @@ void BytecodeGraphBuilder::BuildBinaryOp( ...@@ -520,12 +589,7 @@ void BytecodeGraphBuilder::BuildBinaryOp(
Node* node = NewNode(js_op, left, right); Node* node = NewNode(js_op, left, right);
// TODO(oth): Real frame state and environment check pointing. // TODO(oth): Real frame state and environment check pointing.
int frame_state_count = AddEmptyFrameStateInputs(node);
OperatorProperties::GetFrameStateInputCount(node->op());
for (int i = 0; i < frame_state_count; i++) {
NodeProperties::ReplaceFrameStateInput(node, i,
jsgraph()->EmptyFrameState());
}
environment()->BindAccumulator(node); environment()->BindAccumulator(node);
} }
......
...@@ -34,8 +34,25 @@ class BytecodeGraphBuilder { ...@@ -34,8 +34,25 @@ class BytecodeGraphBuilder {
Node* LoadAccumulator(Node* value); Node* LoadAccumulator(Node* value);
// Get or create the node that represents the outer function closure.
Node* GetFunctionClosure();
// Get or create the node that represents the outer function context.
Node* GetFunctionContext(); Node* GetFunctionContext();
// Builders for accessing an immutable object field.
Node* BuildLoadImmutableObjectField(Node* object, int offset);
// Builder for accessing type feedback vector.
Node* BuildLoadFeedbackVector();
// Helper function for creating a pair containing type feedback vector and
// a feedback slot.
VectorSlotPair CreateVectorSlotPair(int slot_id);
// Replaces the frame state inputs with empty frame states.
void AddEmptyFrameStateInputs(Node* node);
void set_environment(Environment* env) { environment_ = env; } void set_environment(Environment* env) { environment_ = env; }
const Environment* environment() const { return environment_; } const Environment* environment() const { return environment_; }
Environment* environment() { return environment_; } Environment* environment() { return environment_; }
...@@ -55,6 +72,11 @@ class BytecodeGraphBuilder { ...@@ -55,6 +72,11 @@ class BytecodeGraphBuilder {
return MakeNode(op, arraysize(buffer), buffer, false); return MakeNode(op, arraysize(buffer), buffer, false);
} }
Node* NewNode(const Operator* op, Node* n1, Node* n2, Node* n3) {
Node* buffer[] = {n1, n2, n3};
return MakeNode(op, arraysize(buffer), buffer, false);
}
Node* MakeNode(const Operator* op, int value_input_count, Node** value_inputs, Node* MakeNode(const Operator* op, int value_input_count, Node** value_inputs,
bool incomplete); bool incomplete);
...@@ -67,6 +89,8 @@ class BytecodeGraphBuilder { ...@@ -67,6 +89,8 @@ class BytecodeGraphBuilder {
void BuildBinaryOp(const Operator* op, void BuildBinaryOp(const Operator* op,
const interpreter::BytecodeArrayIterator& iterator); const interpreter::BytecodeArrayIterator& iterator);
void BuildNamedLoad(const interpreter::BytecodeArrayIterator& iterator);
// Growth increment for the temporary buffer used to construct input lists to // Growth increment for the temporary buffer used to construct input lists to
// new nodes. // new nodes.
static const int kInputBufferSizeIncrement = 64; static const int kInputBufferSizeIncrement = 64;
...@@ -83,8 +107,8 @@ class BytecodeGraphBuilder { ...@@ -83,8 +107,8 @@ class BytecodeGraphBuilder {
} }
LanguageMode language_mode() const { LanguageMode language_mode() const {
// TODO(oth): need to propagate language mode through // TODO(mythria): Don't rely on parse information to get language mode.
return LanguageMode::SLOPPY; return info()->language_mode();
} }
#define DECLARE_VISIT_BYTECODE(name, ...) \ #define DECLARE_VISIT_BYTECODE(name, ...) \
...@@ -104,6 +128,10 @@ class BytecodeGraphBuilder { ...@@ -104,6 +128,10 @@ class BytecodeGraphBuilder {
// Nodes representing values in the activation record. // Nodes representing values in the activation record.
SetOncePointer<Node> function_context_; SetOncePointer<Node> function_context_;
SetOncePointer<Node> function_closure_;
// Optimization to cache loaded feedback vector.
SetOncePointer<Node> feedback_vector_;
// Control nodes that exit the function body. // Control nodes that exit the function body.
ZoneVector<Node*> exit_controls_; ZoneVector<Node*> exit_controls_;
......
...@@ -453,7 +453,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral( ...@@ -453,7 +453,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
int literal_index, int flags) { int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bytes. DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits.
if (FitsInIdx8Operand(literal_index)) { if (FitsInIdx8Operand(literal_index)) {
Output(Bytecode::kCreateArrayLiteral, static_cast<uint8_t>(literal_index), Output(Bytecode::kCreateArrayLiteral, static_cast<uint8_t>(literal_index),
static_cast<uint8_t>(flags)); static_cast<uint8_t>(flags));
...@@ -466,7 +466,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( ...@@ -466,7 +466,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral( BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
int literal_index, int flags) { int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bytes. DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits.
if (FitsInIdx8Operand(literal_index)) { if (FitsInIdx8Operand(literal_index)) {
Output(Bytecode::kCreateObjectLiteral, static_cast<uint8_t>(literal_index), Output(Bytecode::kCreateObjectLiteral, static_cast<uint8_t>(literal_index),
static_cast<uint8_t>(flags)); static_cast<uint8_t>(flags));
......
...@@ -81,6 +81,10 @@ class BytecodeGraphTester { ...@@ -81,6 +81,10 @@ class BytecodeGraphTester {
return BytecodeGraphCallable<A...>(isolate_, GetFunction()); return BytecodeGraphCallable<A...>(isolate_, GetFunction());
} }
static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
private: private:
Isolate* isolate_; Isolate* isolate_;
Zone* zone_; Zone* zone_;
...@@ -100,8 +104,8 @@ class BytecodeGraphTester { ...@@ -100,8 +104,8 @@ class BytecodeGraphTester {
CompilationInfo compilation_info(&parse_info); CompilationInfo compilation_info(&parse_info);
compilation_info.SetOptimizing(BailoutId::None(), Handle<Code>()); compilation_info.SetOptimizing(BailoutId::None(), Handle<Code>());
Parser parser(&parse_info); // TODO(mythria): Remove this step once parse_info is not needed.
CHECK(parser.Parse(&parse_info)); CHECK(Compiler::ParseAndAnalyze(&parse_info));
compiler::Pipeline pipeline(&compilation_info); compiler::Pipeline pipeline(&compilation_info);
Handle<Code> code = pipeline.GenerateCode(); Handle<Code> code = pipeline.GenerateCode();
function->ReplaceCode(*code); function->ReplaceCode(*code);
...@@ -113,6 +117,36 @@ class BytecodeGraphTester { ...@@ -113,6 +117,36 @@ class BytecodeGraphTester {
}; };
#define SPACE()
#define REPEAT_2(SEP, ...) __VA_ARGS__ SEP() __VA_ARGS__
#define REPEAT_4(SEP, ...) \
REPEAT_2(SEP, __VA_ARGS__) SEP() REPEAT_2(SEP, __VA_ARGS__)
#define REPEAT_8(SEP, ...) \
REPEAT_4(SEP, __VA_ARGS__) SEP() REPEAT_4(SEP, __VA_ARGS__)
#define REPEAT_16(SEP, ...) \
REPEAT_8(SEP, __VA_ARGS__) SEP() REPEAT_8(SEP, __VA_ARGS__)
#define REPEAT_32(SEP, ...) \
REPEAT_16(SEP, __VA_ARGS__) SEP() REPEAT_16(SEP, __VA_ARGS__)
#define REPEAT_64(SEP, ...) \
REPEAT_32(SEP, __VA_ARGS__) SEP() REPEAT_32(SEP, __VA_ARGS__)
#define REPEAT_128(SEP, ...) \
REPEAT_64(SEP, __VA_ARGS__) SEP() REPEAT_64(SEP, __VA_ARGS__)
#define REPEAT_256(SEP, ...) \
REPEAT_128(SEP, __VA_ARGS__) SEP() REPEAT_128(SEP, __VA_ARGS__)
#define REPEAT_127(SEP, ...) \
REPEAT_64(SEP, __VA_ARGS__) \
SEP() \
REPEAT_32(SEP, __VA_ARGS__) \
SEP() \
REPEAT_16(SEP, __VA_ARGS__) \
SEP() \
REPEAT_8(SEP, __VA_ARGS__) \
SEP() \
REPEAT_4(SEP, __VA_ARGS__) SEP() REPEAT_2(SEP, __VA_ARGS__) SEP() __VA_ARGS__
template <int N> template <int N>
struct ExpectedSnippet { struct ExpectedSnippet {
const char* code_snippet; const char* code_snippet;
...@@ -254,6 +288,50 @@ TEST(BytecodeGraphBuilderTwoParameterTests) { ...@@ -254,6 +288,50 @@ TEST(BytecodeGraphBuilderTwoParameterTests) {
} }
} }
TEST(BytecodeGraphBuilderNamedLoad) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<1> snippets[] = {
{"return p1.val;",
{factory->NewNumberFromInt(10),
BytecodeGraphTester::NewObject("({val : 10})")}},
{"return p1[\"name\"];",
{factory->NewStringFromStaticChars("abc"),
BytecodeGraphTester::NewObject("({name : 'abc'})")}},
{"'use strict'; return p1.val;",
{factory->NewNumberFromInt(10),
BytecodeGraphTester::NewObject("({val : 10 })")}},
{"'use strict'; return p1[\"val\"];",
{factory->NewNumberFromInt(10),
BytecodeGraphTester::NewObject("({val : 10, name : 'abc'})")}},
{"var b;\n" REPEAT_127(SPACE, " b = p1.name; ") " return p1.name;\n",
{factory->NewStringFromStaticChars("abc"),
BytecodeGraphTester::NewObject("({name : 'abc'})")}},
{"'use strict'; var b;\n"
REPEAT_127(SPACE, " b = p1.name; ")
"return p1.name;\n",
{factory->NewStringFromStaticChars("abc"),
BytecodeGraphTester::NewObject("({ name : 'abc'})")}},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
ScopedVector<char> script(2048);
SNPrintF(script, "function %s(p1) { %s };\n%s(0);", 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -1404,6 +1404,7 @@ class IsUnopMatcher final : public NodeMatcher { ...@@ -1404,6 +1404,7 @@ class IsUnopMatcher final : public NodeMatcher {
const Matcher<Node*> input_matcher_; const Matcher<Node*> input_matcher_;
}; };
class IsParameterMatcher final : public NodeMatcher { class IsParameterMatcher final : public NodeMatcher {
public: public:
explicit IsParameterMatcher(const Matcher<int>& index_matcher) explicit IsParameterMatcher(const Matcher<int>& index_matcher)
...@@ -1425,6 +1426,60 @@ class IsParameterMatcher final : public NodeMatcher { ...@@ -1425,6 +1426,60 @@ class IsParameterMatcher final : public NodeMatcher {
const Matcher<int> index_matcher_; const Matcher<int> index_matcher_;
}; };
// TODO(mythria): Check if we can use the same matcher for Load and Store
class IsJSLoadNamedMatcher final : public NodeMatcher {
public:
IsJSLoadNamedMatcher(const Matcher<Handle<Name>>& name_matcher,
const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& feedback_vector_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher)
: NodeMatcher(IrOpcode::kJSLoadNamed),
name_matcher_(name_matcher),
object_value_matcher_(object_value_matcher),
feedback_vector_matcher_(feedback_vector_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 << "), name (";
name_matcher_.DescribeTo(os);
*os << "), feedback vector (";
feedback_vector_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(OpParameter<const NamedAccess>(node).name(),
"Name", name_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
"object", object_value_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
"feedback vector", feedback_vector_matcher_,
listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
effect_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetControlInput(node),
"control", control_matcher_, listener));
}
private:
const Matcher<Handle<Name>> name_matcher_;
const Matcher<Node*> object_value_matcher_;
const Matcher<Node*> feedback_vector_matcher_;
const Matcher<Node*> effect_matcher_;
const Matcher<Node*> control_matcher_;
};
} // namespace } // namespace
...@@ -1998,6 +2053,17 @@ Matcher<Node*> IsLoadFramePointer() { ...@@ -1998,6 +2053,17 @@ Matcher<Node*> IsLoadFramePointer() {
} }
Matcher<Node*> IsJSLoadNamed(const Handle<Name> name,
const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& feedback_vector_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsJSLoadNamedMatcher(name, object_value_matcher,
feedback_vector_matcher,
effect_matcher, control_matcher));
}
#define IS_BINOP_MATCHER(Name) \ #define IS_BINOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \ Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& rhs_matcher) { \ const Matcher<Node*>& rhs_matcher) { \
......
...@@ -353,6 +353,11 @@ Matcher<Node*> IsNumberToInt32(const Matcher<Node*>& input_matcher); ...@@ -353,6 +353,11 @@ Matcher<Node*> IsNumberToInt32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher); Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsParameter(const Matcher<int> index_matcher); Matcher<Node*> IsParameter(const Matcher<int> index_matcher);
Matcher<Node*> IsLoadFramePointer(); Matcher<Node*> IsLoadFramePointer();
Matcher<Node*> IsJSLoadNamed(const Handle<Name> name,
const Matcher<Node*>& object_value_matcher,
const Matcher<Node*>& feedback_vector_matcher,
const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher);
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
......
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