[turbofan] Pass deoptimization mode to type feedback specializer.

R=jarin@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#28524}
This commit is contained in:
bmeurer 2015-05-20 10:01:31 -07:00 committed by Commit bot
parent 1ec5561685
commit 4a1ab1cac4
4 changed files with 224 additions and 155 deletions

View File

@ -173,7 +173,7 @@ Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) {
return ReduceJSLoadNamedForGlobalVariable(node);
}
if (!FLAG_turbo_deoptimization) return NoChange();
if (mode() != kDeoptimizationEnabled) return NoChange();
Node* frame_state_before = GetFrameStateBefore(node);
if (frame_state_before == nullptr) return NoChange();
@ -245,7 +245,7 @@ Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamedForGlobalVariable(
return NoChange();
}
if (FLAG_turbo_deoptimization) {
if (mode() == kDeoptimizationEnabled) {
// Handle lookups in the script context.
{
Handle<ScriptContextTable> script_contexts(

View File

@ -62,10 +62,13 @@ class JSTypeFeedbackTable : public ZoneObject {
// {js_type_feedback} provided to the constructor.
class JSTypeFeedbackSpecializer : public AdvancedReducer {
public:
enum DeoptimizationMode { kDeoptimizationEnabled, kDeoptimizationDisabled };
JSTypeFeedbackSpecializer(Editor* editor, JSGraph* jsgraph,
JSTypeFeedbackTable* js_type_feedback,
TypeFeedbackOracle* oracle,
Handle<GlobalObject> global_object,
DeoptimizationMode mode,
CompilationDependencies* dependencies)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
@ -73,8 +76,9 @@ class JSTypeFeedbackSpecializer : public AdvancedReducer {
js_type_feedback_(js_type_feedback),
oracle_(oracle),
global_object_(global_object),
mode_(mode),
dependencies_(dependencies) {
CHECK(js_type_feedback);
CHECK_NOT_NULL(js_type_feedback);
}
Reduction Reduce(Node* node) override;
@ -92,12 +96,14 @@ class JSTypeFeedbackSpecializer : public AdvancedReducer {
JSTypeFeedbackTable* js_type_feedback_;
TypeFeedbackOracle* oracle_;
Handle<GlobalObject> global_object_;
DeoptimizationMode const mode_;
CompilationDependencies* dependencies_;
TypeFeedbackOracle* oracle() { return oracle_; }
Graph* graph() { return jsgraph_->graph(); }
JSGraph* jsgraph() { return jsgraph_; }
CommonOperatorBuilder* common() { return jsgraph_->common(); }
DeoptimizationMode mode() const { return mode_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
void BuildMapCheck(Node* receiver, Handle<Map> map, bool smi_check,

View File

@ -546,7 +546,10 @@ struct JSTypeFeedbackPhase {
// specializing to the global object here.
JSTypeFeedbackSpecializer specializer(
&graph_reducer, data->jsgraph(), data->js_type_feedback(), &oracle,
global_object, data->info()->dependencies());
global_object, data->info()->is_deoptimization_enabled()
? JSTypeFeedbackSpecializer::kDeoptimizationEnabled
: JSTypeFeedbackSpecializer::kDeoptimizationDisabled,
data->info()->dependencies());
AddReducer(data, &graph_reducer, &specializer);
graph_reducer.ReduceGraph();
}
@ -1048,9 +1051,7 @@ Handle<Code> Pipeline::GenerateCode() {
RunPrintAndVerify("OSR deconstruction");
}
// TODO(turbofan): Type feedback currently requires deoptimization.
if (info()->is_deoptimization_enabled() &&
info()->is_type_feedback_enabled()) {
if (info()->is_type_feedback_enabled()) {
Run<JSTypeFeedbackPhase>();
RunPrintAndVerify("JSType feedback");
}

View File

@ -34,7 +34,8 @@ class JSTypeFeedbackTest : public TypedGraphTest {
~JSTypeFeedbackTest() override { dependencies_.Rollback(); }
protected:
Reduction Reduce(Node* node) {
Reduction Reduce(Node* node,
JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
Handle<GlobalObject> global_object(
isolate()->native_context()->global_object(), isolate());
@ -44,7 +45,7 @@ class JSTypeFeedbackTest : public TypedGraphTest {
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(graph(), zone());
JSTypeFeedbackSpecializer reducer(&graph_reducer, &jsgraph, &table, nullptr,
global_object, &dependencies_);
global_object, mode, &dependencies_);
return reducer.Reduce(node);
}
@ -73,8 +74,9 @@ class JSTypeFeedbackTest : public TypedGraphTest {
result.Assert();
}
Node* ReturnLoadNamedFromGlobal(const char* string, Node* effect,
Node* control) {
Node* ReturnLoadNamedFromGlobal(
const char* string, Node* effect, Node* control,
JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
VectorSlotPair feedback(Handle<TypeFeedbackVector>::null(),
FeedbackVectorICSlot::Invalid());
Node* global = Parameter(Type::GlobalObject());
@ -84,7 +86,7 @@ class JSTypeFeedbackTest : public TypedGraphTest {
isolate()->factory()->NewStringFromAsciiChecked(string));
const Operator* op = javascript()->LoadNamed(name, feedback);
Node* load = graph()->NewNode(op, global, context);
if (FLAG_turbo_deoptimization) {
if (mode == JSTypeFeedbackSpecializer::kDeoptimizationEnabled) {
for (int i = 0; i < OperatorProperties::GetFrameStateInputCount(op);
i++) {
load->AppendInput(zone(), EmptyFrameState());
@ -103,180 +105,240 @@ class JSTypeFeedbackTest : public TypedGraphTest {
CompilationDependencies dependencies_;
};
#define WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION \
for (int i = FLAG_turbo_deoptimization = 0; i < 2; \
FLAG_turbo_deoptimization = ++i)
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmi) {
const int kValue = 111;
const char* kName = "banana";
SetGlobalProperty(kName, kValue);
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConst_smi) {
const int const_value = 111;
const char* property_name = "banana";
SetGlobalProperty(property_name, const_value);
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION {
Node* ret = ReturnLoadNamedFromGlobal(property_name, graph()->start(),
graph()->start());
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0));
if (FLAG_turbo_deoptimization) {
// Check LoadNamed(global) => HeapConstant[const_value]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(const_value));
EXPECT_THAT(ret, IsReturn(IsNumberConstant(const_value), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
} else {
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
}
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
EXPECT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConst_derble) {
const double const_value = -11.25;
const char* property_name = "kiwi";
SetGlobalProperty(property_name, const_value);
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmiWithDeoptimization) {
const int kValue = 111;
const char* kName = "banana";
SetGlobalProperty(kName, kValue);
WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION {
Node* ret = ReturnLoadNamedFromGlobal(property_name, graph()->start(),
graph()->start());
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
if (FLAG_turbo_deoptimization) {
// Check LoadNamed(global) => HeapConstant[const_value]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(const_value));
// Check LoadNamed(global) => HeapConstant[kValue]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
EXPECT_THAT(ret, IsReturn(IsNumberConstant(const_value), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
} else {
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
}
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConst_string) {
Unique<HeapObject> const_value = Unique<HeapObject>::CreateImmovable(
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumber) {
const double kValue = -11.25;
const char* kName = "kiwi";
SetGlobalProperty(kName, kValue);
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
EXPECT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumberWithDeoptimization) {
const double kValue = -11.25;
const char* kName = "kiwi";
SetGlobalProperty(kName, kValue);
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
// Check LoadNamed(global) => HeapConstant[kValue]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstString) {
Unique<HeapObject> kValue = Unique<HeapObject>::CreateImmovable(
isolate()->factory()->undefined_string());
const char* property_name = "mango";
SetGlobalProperty(property_name, const_value.handle());
const char* kName = "mango";
SetGlobalProperty(kName, kValue.handle());
WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION {
Node* ret = ReturnLoadNamedFromGlobal(property_name, graph()->start(),
graph()->start());
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0));
if (FLAG_turbo_deoptimization) {
// Check LoadNamed(global) => HeapConstant[const_value]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(const_value));
EXPECT_THAT(ret, IsReturn(IsHeapConstant(const_value), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
} else {
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
}
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCell_smi) {
const char* property_name = "melon";
SetGlobalProperty(property_name, 123);
SetGlobalProperty(property_name, 124);
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstStringWithDeoptimization) {
Unique<HeapObject> kValue = Unique<HeapObject>::CreateImmovable(
isolate()->factory()->undefined_string());
const char* kName = "mango";
SetGlobalProperty(kName, kValue.handle());
WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION {
Node* ret = ReturnLoadNamedFromGlobal(property_name, graph()->start(),
graph()->start());
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
if (FLAG_turbo_deoptimization) {
// Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
ASSERT_TRUE(r.Changed());
FieldAccess access = AccessBuilder::ForPropertyCellValue();
Capture<Node*> cell_capture;
Matcher<Node*> load_field_match = IsLoadField(
access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
EXPECT_THAT(r.replacement(), load_field_match);
// Check LoadNamed(global) => HeapConstant[kValue]
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(kValue));
HeapObjectMatcher<PropertyCell> cell(cell_capture.value());
EXPECT_TRUE(cell.HasValue());
EXPECT_TRUE(cell.Value().handle()->IsPropertyCell());
EXPECT_THAT(ret, IsReturn(IsHeapConstant(kValue), graph()->start(),
graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_THAT(
ret, IsReturn(load_field_match, load_field_match, graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
} else {
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
}
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCell_string) {
const char* property_name = "pineapple";
SetGlobalProperty(property_name, isolate()->factory()->undefined_string());
SetGlobalProperty(property_name, isolate()->factory()->undefined_value());
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmi) {
const char* kName = "melon";
SetGlobalProperty(kName, 123);
SetGlobalProperty(kName, 124);
WITH_AND_WITHOUT_TURBO_DEOPTIMIZATION {
Node* ret = ReturnLoadNamedFromGlobal(property_name, graph()->start(),
graph()->start());
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0));
if (FLAG_turbo_deoptimization) {
// Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
ASSERT_TRUE(r.Changed());
FieldAccess access = AccessBuilder::ForPropertyCellValue();
Capture<Node*> cell_capture;
Matcher<Node*> load_field_match = IsLoadField(
access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
EXPECT_THAT(r.replacement(), load_field_match);
HeapObjectMatcher<PropertyCell> cell(cell_capture.value());
EXPECT_TRUE(cell.HasValue());
EXPECT_TRUE(cell.Value().handle()->IsPropertyCell());
EXPECT_THAT(
ret, IsReturn(load_field_match, load_field_match, graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
} else {
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
}
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmiWithDeoptimization) {
const char* kName = "melon";
SetGlobalProperty(kName, 123);
SetGlobalProperty(kName, 124);
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
// Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
ASSERT_TRUE(r.Changed());
FieldAccess access = AccessBuilder::ForPropertyCellValue();
Capture<Node*> cell_capture;
Matcher<Node*> load_field_match = IsLoadField(
access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
EXPECT_THAT(r.replacement(), load_field_match);
HeapObjectMatcher<PropertyCell> cell(cell_capture.value());
EXPECT_TRUE(cell.HasValue());
EXPECT_TRUE(cell.Value().handle()->IsPropertyCell());
EXPECT_THAT(ret,
IsReturn(load_field_match, load_field_match, graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
}
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellString) {
const char* kName = "pineapple";
SetGlobalProperty(kName, isolate()->factory()->undefined_string());
SetGlobalProperty(kName, isolate()->factory()->undefined_value());
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
ASSERT_FALSE(r.Changed());
EXPECT_TRUE(dependencies()->IsEmpty());
}
TEST_F(JSTypeFeedbackTest,
JSLoadNamedGlobalPropertyCellStringWithDeoptimization) {
const char* kName = "pineapple";
SetGlobalProperty(kName, isolate()->factory()->undefined_string());
SetGlobalProperty(kName, isolate()->factory()->undefined_value());
Node* ret = ReturnLoadNamedFromGlobal(
kName, graph()->start(), graph()->start(),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
graph()->SetEnd(graph()->NewNode(common()->End(), ret));
Reduction r = Reduce(ret->InputAt(0),
JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
// Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
ASSERT_TRUE(r.Changed());
FieldAccess access = AccessBuilder::ForPropertyCellValue();
Capture<Node*> cell_capture;
Matcher<Node*> load_field_match = IsLoadField(
access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
EXPECT_THAT(r.replacement(), load_field_match);
HeapObjectMatcher<PropertyCell> cell(cell_capture.value());
EXPECT_TRUE(cell.HasValue());
EXPECT_TRUE(cell.Value().handle()->IsPropertyCell());
EXPECT_THAT(ret,
IsReturn(load_field_match, load_field_match, graph()->start()));
EXPECT_THAT(graph()->end(), IsEnd(ret));
EXPECT_FALSE(dependencies()->IsEmpty());
dependencies()->Rollback();
}
} // namespace compiler
} // namespace internal
} // namespace v8