4bb6e7c8c0
bytecode graph builder Adds implementation and tests for KeyedLoadIC, KeyedStoreIC and StoreIC to bytecode graph builder. BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1448913002 Cr-Commit-Position: refs/heads/master@{#32116}
727 lines
26 KiB
C++
727 lines
26 KiB
C++
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <iostream>
|
|
|
|
#include "src/compiler/bytecode-graph-builder.h"
|
|
#include "src/compiler/common-operator.h"
|
|
#include "src/compiler/graph-visualizer.h"
|
|
#include "src/compiler/instruction.h"
|
|
#include "src/compiler/instruction-selector.h"
|
|
#include "src/compiler/js-graph.h"
|
|
#include "src/compiler/js-operator.h"
|
|
#include "src/compiler/linkage.h"
|
|
#include "src/interpreter/bytecode-array-builder.h"
|
|
#include "src/parser.h"
|
|
#include "test/unittests/compiler/compiler-test-utils.h"
|
|
#include "test/unittests/compiler/graph-unittest.h"
|
|
#include "test/unittests/compiler/node-test-utils.h"
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
using ::testing::_;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
static const LanguageMode kLanguageModes[] = {LanguageMode::SLOPPY,
|
|
LanguageMode::STRICT};
|
|
|
|
Handle<TypeFeedbackVector> NewTypeFeedbackVector(Isolate* isolate,
|
|
FeedbackVectorSpec* spec) {
|
|
Handle<TypeFeedbackMetadata> vector_metadata =
|
|
TypeFeedbackMetadata::New(isolate, spec);
|
|
return TypeFeedbackVector::New(isolate, vector_metadata);
|
|
}
|
|
|
|
|
|
class BytecodeGraphBuilderTest : public TestWithIsolateAndZone {
|
|
public:
|
|
BytecodeGraphBuilderTest() {}
|
|
|
|
Graph* GetCompletedGraph(Handle<BytecodeArray> bytecode_array,
|
|
MaybeHandle<TypeFeedbackVector> feedback_vector =
|
|
MaybeHandle<TypeFeedbackVector>(),
|
|
LanguageMode language_mode = LanguageMode::SLOPPY);
|
|
|
|
Matcher<Node*> IsUndefinedConstant();
|
|
Matcher<Node*> IsNullConstant();
|
|
Matcher<Node*> IsTheHoleConstant();
|
|
Matcher<Node*> IsFalseConstant();
|
|
Matcher<Node*> IsTrueConstant();
|
|
Matcher<Node*> IsIntPtrConstant(int value);
|
|
Matcher<Node*> IsFeedbackVector(Node* effect, Node* control);
|
|
|
|
static Handle<String> GetName(Isolate* isolate, const char* name) {
|
|
Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(name);
|
|
return isolate->factory()->string_table()->LookupString(isolate, result);
|
|
}
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(BytecodeGraphBuilderTest);
|
|
};
|
|
|
|
|
|
Graph* BytecodeGraphBuilderTest::GetCompletedGraph(
|
|
Handle<BytecodeArray> bytecode_array,
|
|
MaybeHandle<TypeFeedbackVector> feedback_vector,
|
|
LanguageMode language_mode) {
|
|
MachineOperatorBuilder* machine = new (zone()) MachineOperatorBuilder(
|
|
zone(), kMachPtr, InstructionSelector::SupportedMachineOperatorFlags());
|
|
CommonOperatorBuilder* common = new (zone()) CommonOperatorBuilder(zone());
|
|
JSOperatorBuilder* javascript = new (zone()) JSOperatorBuilder(zone());
|
|
Graph* graph = new (zone()) Graph(zone());
|
|
JSGraph* jsgraph = new (zone())
|
|
JSGraph(isolate(), graph, common, javascript, nullptr, machine);
|
|
|
|
Handle<String> name = factory()->NewStringFromStaticChars("test");
|
|
Handle<String> script = factory()->NewStringFromStaticChars("test() {}");
|
|
Handle<SharedFunctionInfo> shared_info =
|
|
factory()->NewSharedFunctionInfo(name, MaybeHandle<Code>());
|
|
shared_info->set_script(*factory()->NewScript(script));
|
|
if (!feedback_vector.is_null()) {
|
|
shared_info->set_feedback_vector(*feedback_vector.ToHandleChecked());
|
|
}
|
|
|
|
ParseInfo parse_info(zone(), shared_info);
|
|
parse_info.set_language_mode(language_mode);
|
|
CompilationInfo info(&parse_info);
|
|
info.shared_info()->set_function_data(*bytecode_array);
|
|
|
|
BytecodeGraphBuilder graph_builder(zone(), &info, jsgraph);
|
|
graph_builder.CreateGraph();
|
|
return graph;
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsUndefinedConstant() {
|
|
return IsHeapConstant(factory()->undefined_value());
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsNullConstant() {
|
|
return IsHeapConstant(factory()->null_value());
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsTheHoleConstant() {
|
|
return IsHeapConstant(factory()->the_hole_value());
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsFalseConstant() {
|
|
return IsHeapConstant(factory()->false_value());
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsTrueConstant() {
|
|
return IsHeapConstant(factory()->true_value());
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsIntPtrConstant(int value) {
|
|
if (kPointerSize == 8) {
|
|
return IsInt64Constant(value);
|
|
} else {
|
|
return IsInt32Constant(value);
|
|
}
|
|
}
|
|
|
|
|
|
Matcher<Node*> BytecodeGraphBuilderTest::IsFeedbackVector(Node* effect,
|
|
Node* control) {
|
|
int offset = SharedFunctionInfo::kFeedbackVectorOffset - kHeapObjectTag;
|
|
int offset1 = JSFunction::kSharedFunctionInfoOffset - kHeapObjectTag;
|
|
|
|
return IsLoad(
|
|
kMachAnyTagged,
|
|
IsLoad(kMachAnyTagged, IsParameter(Linkage::kJSCallClosureParamIndex),
|
|
IsIntPtrConstant(offset1), effect, control),
|
|
IsIntPtrConstant(offset), effect, control);
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnUndefined) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadUndefined().Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsUndefinedConstant(), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnNull) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadNull().Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
EXPECT_THAT(ret, IsReturn(IsNullConstant(), graph->start(), graph->start()));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnTheHole) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadTheHole().Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsTheHoleConstant(), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnTrue) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadTrue().Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsTrueConstant(), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnFalse) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadFalse().Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsFalseConstant(), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnInt8) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
static const int kValue = 3;
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadLiteral(Smi::FromInt(kValue)).Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, ReturnDouble) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
const double kValue = 0.123456789;
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadLiteral(factory()->NewHeapNumber(kValue));
|
|
array_builder.Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
Node* effect = graph->start();
|
|
Node* control = graph->start();
|
|
EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), effect, control));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, SimpleExpressionWithParameters) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(1);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(3);
|
|
array_builder.LoadAccumulatorWithRegister(array_builder.Parameter(1))
|
|
.BinaryOperation(Token::Value::ADD, array_builder.Parameter(2),
|
|
Strength::WEAK)
|
|
.StoreAccumulatorInRegister(interpreter::Register(0))
|
|
.Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
// NB binary operation is <reg> <op> <acc>. The register represents
|
|
// the left-hand side, which is why parameters appear in opposite
|
|
// order to construction via the builder.
|
|
EXPECT_THAT(ret, IsReturn(IsJSAdd(IsParameter(2), IsParameter(1)), _, _));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, SimpleExpressionWithRegister) {
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
static const int kLeft = -655371;
|
|
static const int kRight = +2000000;
|
|
array_builder.set_locals_count(1);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
array_builder.LoadLiteral(Smi::FromInt(kLeft))
|
|
.StoreAccumulatorInRegister(interpreter::Register(0))
|
|
.LoadLiteral(Smi::FromInt(kRight))
|
|
.BinaryOperation(Token::Value::ADD, interpreter::Register(0),
|
|
Strength::WEAK)
|
|
.Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray());
|
|
Node* end = graph->end();
|
|
EXPECT_EQ(1, end->InputCount());
|
|
Node* ret = end->InputAt(0);
|
|
EXPECT_THAT(
|
|
ret, IsReturn(IsJSAdd(IsNumberConstant(kLeft), IsNumberConstant(kRight)),
|
|
_, _));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, NamedLoad) {
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddLoadICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
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");
|
|
size_t name_index = array_builder.GetConstantPoolEntry(name);
|
|
|
|
array_builder.LoadNamedProperty(array_builder.Parameter(1), name_index,
|
|
vector->GetIndex(slot), language_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
|
|
language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> load_named_matcher = IsJSLoadNamed(
|
|
name, IsParameter(1), feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(load_named_matcher, _, _));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, CallProperty0) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
FeedbackVectorSlot call_slot = feedback_spec.AddCallICSlot();
|
|
FeedbackVectorSlot load_slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
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> func_name = GetName(isolate(), "func");
|
|
size_t func_name_index = array_builder.GetConstantPoolEntry(func_name);
|
|
|
|
interpreter::Register reg0 = interpreter::Register(0);
|
|
array_builder.LoadNamedProperty(array_builder.Parameter(1), func_name_index,
|
|
vector->GetIndex(load_slot),
|
|
LanguageMode::SLOPPY)
|
|
.StoreAccumulatorInRegister(reg0)
|
|
.Call(reg0, array_builder.Parameter(1), 0, vector->GetIndex(call_slot))
|
|
.Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> load_named_matcher = IsJSLoadNamed(
|
|
func_name, IsParameter(1), feedback_vector_matcher, start, start);
|
|
std::vector<Matcher<Node*>> call_inputs;
|
|
call_inputs.push_back(load_named_matcher);
|
|
call_inputs.push_back(IsParameter(1));
|
|
Matcher<Node*> call_matcher =
|
|
IsJSCallFunction(call_inputs, load_named_matcher, IsIfSuccess(_));
|
|
|
|
EXPECT_THAT(ret, IsReturn(call_matcher, _, _));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, CallProperty2) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
FeedbackVectorSlot call_slot = feedback_spec.AddCallICSlot();
|
|
FeedbackVectorSlot load_slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(4);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(4);
|
|
|
|
Handle<Name> func_name = GetName(isolate(), "func");
|
|
size_t func_name_index = array_builder.GetConstantPoolEntry(func_name);
|
|
|
|
interpreter::Register reg0 = interpreter::Register(0);
|
|
interpreter::Register reg1 = interpreter::Register(1);
|
|
interpreter::Register reg2 = interpreter::Register(2);
|
|
interpreter::Register reg3 = interpreter::Register(3);
|
|
array_builder.LoadNamedProperty(array_builder.Parameter(1), func_name_index,
|
|
vector->GetIndex(load_slot),
|
|
LanguageMode::SLOPPY)
|
|
.StoreAccumulatorInRegister(reg0)
|
|
.LoadAccumulatorWithRegister(array_builder.Parameter(1))
|
|
.StoreAccumulatorInRegister(reg1)
|
|
.LoadAccumulatorWithRegister(array_builder.Parameter(2))
|
|
.StoreAccumulatorInRegister(reg2)
|
|
.LoadAccumulatorWithRegister(array_builder.Parameter(3))
|
|
.StoreAccumulatorInRegister(reg3)
|
|
.Call(reg0, reg1, 2, vector->GetIndex(call_slot))
|
|
.Return();
|
|
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector);
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> load_named_matcher = IsJSLoadNamed(
|
|
func_name, IsParameter(1), feedback_vector_matcher, start, start);
|
|
std::vector<Matcher<Node*>> call_inputs;
|
|
call_inputs.push_back(load_named_matcher);
|
|
call_inputs.push_back(IsParameter(1));
|
|
call_inputs.push_back(IsParameter(2));
|
|
call_inputs.push_back(IsParameter(3));
|
|
Matcher<Node*> call_matcher =
|
|
IsJSCallFunction(call_inputs, load_named_matcher, IsIfSuccess(_));
|
|
|
|
EXPECT_THAT(ret, IsReturn(call_matcher, _, _));
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, LoadGlobal) {
|
|
const TypeofMode kTypeOfModes[] = {TypeofMode::NOT_INSIDE_TYPEOF,
|
|
TypeofMode::INSIDE_TYPEOF};
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(TypeofMode, typeof_mode, kTypeOfModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddLoadICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
|
|
Handle<Name> name = GetName(isolate(), "global");
|
|
size_t name_index = array_builder.GetConstantPoolEntry(name);
|
|
|
|
array_builder.LoadGlobal(name_index, vector->GetIndex(slot),
|
|
language_mode, typeof_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(),
|
|
vector, language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> load_global_matcher = IsJSLoadGlobal(
|
|
name, typeof_mode, feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(load_global_matcher, _, _));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, StoreGlobal) {
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddStoreICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
interpreter::BytecodeArrayBuilder array_builder(isolate(), zone());
|
|
array_builder.set_locals_count(0);
|
|
array_builder.set_context_count(0);
|
|
array_builder.set_parameter_count(1);
|
|
|
|
Handle<Name> name = GetName(isolate(), "global");
|
|
size_t name_index = array_builder.GetConstantPoolEntry(name);
|
|
|
|
array_builder.LoadLiteral(Smi::FromInt(321))
|
|
.StoreGlobal(name_index, vector->GetIndex(slot), language_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
|
|
language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> value_matcher = IsNumberConstant(321);
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> store_global_matcher = IsJSStoreGlobal(
|
|
name, value_matcher, feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(_, store_global_matcher, _));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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, _, _));
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, KeyedLoad) {
|
|
const int kValue = 100;
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddLoadICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
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.LoadLiteral(Smi::FromInt(kValue))
|
|
.LoadKeyedProperty(array_builder.Parameter(1), vector->GetIndex(slot),
|
|
language_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
|
|
language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> load_keyed_matcher =
|
|
IsJSLoadProperty(IsParameter(1), IsNumberConstant(kValue),
|
|
feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(load_keyed_matcher, _, _));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, NamedStore) {
|
|
const int kValue = 100;
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddLoadICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
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");
|
|
size_t name_index = array_builder.GetConstantPoolEntry(name);
|
|
|
|
array_builder.LoadLiteral(Smi::FromInt(kValue))
|
|
.StoreNamedProperty(array_builder.Parameter(1), name_index,
|
|
vector->GetIndex(slot), language_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
|
|
language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> store_named_matcher =
|
|
IsJSStoreNamed(name, IsParameter(1), IsNumberConstant(kValue),
|
|
feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(_, store_named_matcher, _));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(BytecodeGraphBuilderTest, KeyedStore) {
|
|
const int kValue = 100;
|
|
const int kKey = 10;
|
|
const bool kWideBytecode[] = {false, true};
|
|
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
|
TRACED_FOREACH(bool, wide_bytecode, kWideBytecode) {
|
|
FeedbackVectorSpec feedback_spec(zone());
|
|
if (wide_bytecode) {
|
|
for (int i = 0; i < 128; i++) {
|
|
feedback_spec.AddStoreICSlot();
|
|
}
|
|
}
|
|
FeedbackVectorSlot slot = feedback_spec.AddStoreICSlot();
|
|
Handle<TypeFeedbackVector> vector =
|
|
NewTypeFeedbackVector(isolate(), &feedback_spec);
|
|
|
|
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.LoadLiteral(Smi::FromInt(kKey))
|
|
.StoreAccumulatorInRegister(interpreter::Register(0))
|
|
.LoadLiteral(Smi::FromInt(kValue))
|
|
.StoreKeyedProperty(array_builder.Parameter(1),
|
|
interpreter::Register(0), vector->GetIndex(slot),
|
|
language_mode)
|
|
.Return();
|
|
Graph* graph = GetCompletedGraph(array_builder.ToBytecodeArray(), vector,
|
|
language_mode);
|
|
|
|
Node* ret = graph->end()->InputAt(0);
|
|
Node* start = graph->start();
|
|
|
|
Matcher<Node*> feedback_vector_matcher = IsFeedbackVector(start, start);
|
|
Matcher<Node*> store_keyed_matcher = IsJSStoreProperty(
|
|
IsParameter(1), IsNumberConstant(kKey), IsNumberConstant(kValue),
|
|
feedback_vector_matcher, start, start);
|
|
|
|
EXPECT_THAT(ret, IsReturn(_, store_keyed_matcher, _));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|