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() {
}
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) {
// Set up the basic structure of the graph. Outputs for {Start} are
// the formal parameters (including the receiver) plus context and
......@@ -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(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_sloppy(language_mode()));
BuildNamedLoad(iterator);
}
void BytecodeGraphBuilder::VisitLoadICStrict(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_strict(language_mode()));
BuildNamedLoad(iterator);
}
......@@ -365,13 +432,15 @@ void BytecodeGraphBuilder::VisitKeyedLoadICStrict(
void BytecodeGraphBuilder::VisitLoadICSloppyWide(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_sloppy(language_mode()));
BuildNamedLoad(iterator);
}
void BytecodeGraphBuilder::VisitLoadICStrictWide(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
DCHECK(is_strict(language_mode()));
BuildNamedLoad(iterator);
}
......@@ -520,12 +589,7 @@ void BytecodeGraphBuilder::BuildBinaryOp(
Node* node = NewNode(js_op, left, right);
// TODO(oth): Real frame state and environment check pointing.
int frame_state_count =
OperatorProperties::GetFrameStateInputCount(node->op());
for (int i = 0; i < frame_state_count; i++) {
NodeProperties::ReplaceFrameStateInput(node, i,
jsgraph()->EmptyFrameState());
}
AddEmptyFrameStateInputs(node);
environment()->BindAccumulator(node);
}
......
......@@ -34,8 +34,25 @@ class BytecodeGraphBuilder {
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();
// 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; }
const Environment* environment() const { return environment_; }
Environment* environment() { return environment_; }
......@@ -55,6 +72,11 @@ class BytecodeGraphBuilder {
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,
bool incomplete);
......@@ -67,6 +89,8 @@ class BytecodeGraphBuilder {
void BuildBinaryOp(const Operator* op,
const interpreter::BytecodeArrayIterator& iterator);
void BuildNamedLoad(const interpreter::BytecodeArrayIterator& iterator);
// Growth increment for the temporary buffer used to construct input lists to
// new nodes.
static const int kInputBufferSizeIncrement = 64;
......@@ -83,8 +107,8 @@ class BytecodeGraphBuilder {
}
LanguageMode language_mode() const {
// TODO(oth): need to propagate language mode through
return LanguageMode::SLOPPY;
// TODO(mythria): Don't rely on parse information to get language mode.
return info()->language_mode();
}
#define DECLARE_VISIT_BYTECODE(name, ...) \
......@@ -104,6 +128,10 @@ class BytecodeGraphBuilder {
// Nodes representing values in the activation record.
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.
ZoneVector<Node*> exit_controls_;
......
......@@ -453,7 +453,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
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)) {
Output(Bytecode::kCreateArrayLiteral, static_cast<uint8_t>(literal_index),
static_cast<uint8_t>(flags));
......@@ -466,7 +466,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
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)) {
Output(Bytecode::kCreateObjectLiteral, static_cast<uint8_t>(literal_index),
static_cast<uint8_t>(flags));
......
......@@ -81,6 +81,10 @@ class BytecodeGraphTester {
return BytecodeGraphCallable<A...>(isolate_, GetFunction());
}
static Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
private:
Isolate* isolate_;
Zone* zone_;
......@@ -100,8 +104,8 @@ class BytecodeGraphTester {
CompilationInfo compilation_info(&parse_info);
compilation_info.SetOptimizing(BailoutId::None(), Handle<Code>());
Parser parser(&parse_info);
CHECK(parser.Parse(&parse_info));
// TODO(mythria): Remove this step once parse_info is not needed.
CHECK(Compiler::ParseAndAnalyze(&parse_info));
compiler::Pipeline pipeline(&compilation_info);
Handle<Code> code = pipeline.GenerateCode();
function->ReplaceCode(*code);
......@@ -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>
struct ExpectedSnippet {
const char* code_snippet;
......@@ -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 internal
} // namespace v8
......@@ -1404,6 +1404,7 @@ class IsUnopMatcher final : public NodeMatcher {
const Matcher<Node*> input_matcher_;
};
class IsParameterMatcher final : public NodeMatcher {
public:
explicit IsParameterMatcher(const Matcher<int>& index_matcher)
......@@ -1425,6 +1426,60 @@ class IsParameterMatcher final : public NodeMatcher {
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
......@@ -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) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& rhs_matcher) { \
......
......@@ -353,6 +353,11 @@ Matcher<Node*> IsNumberToInt32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsParameter(const Matcher<int> index_matcher);
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 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