Disable hoisting CheckClass due to excessive deoptimization (issue 16285). Detect that a deoptimization was caused by hoisted check class instruction and disable hoisting of CheckClassInstr for that function. This is a short-term solution to fix excessive deoptimization causing slow start-ups.
R=johnmccutchan@google.com Review URL: https://codereview.chromium.org//157833004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@32460 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -71,6 +71,7 @@ DECLARE_RUNTIME_ENTRY(UpdateFieldCid);
|
||||
V(UnaryOp) \
|
||||
V(UnboxInteger) \
|
||||
V(CheckClass) \
|
||||
V(HoistedCheckClass) \
|
||||
V(CheckSmi) \
|
||||
V(CheckArrayBound) \
|
||||
V(AtCall) \
|
||||
|
||||
@@ -48,8 +48,6 @@ DEFINE_FLAG(bool, allocation_sinking, true,
|
||||
"Attempt to sink temporary allocations to side exits");
|
||||
DEFINE_FLAG(int, deoptimization_counter_threshold, 16,
|
||||
"How many times we allow deoptimization before we disallow optimization.");
|
||||
DEFINE_FLAG(int, deoptimization_counter_licm_threshold, 8,
|
||||
"How many times we allow deoptimization before we disable LICM.");
|
||||
DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph.");
|
||||
DEFINE_FLAG(bool, print_flow_graph_optimized, false,
|
||||
"Print the IR flow graph when optimizing.");
|
||||
@@ -442,9 +440,7 @@ static bool CompileParsedFunctionHelper(ParsedFunction* parsed_function,
|
||||
optimizer.TryOptimizePatterns();
|
||||
DEBUG_ASSERT(flow_graph->VerifyUseLists());
|
||||
|
||||
if (FLAG_loop_invariant_code_motion &&
|
||||
(function.deoptimization_counter() <
|
||||
FLAG_deoptimization_counter_licm_threshold)) {
|
||||
if (FLAG_loop_invariant_code_motion) {
|
||||
LICM licm(flow_graph);
|
||||
licm.Optimize();
|
||||
DEBUG_ASSERT(flow_graph->VerifyUseLists());
|
||||
|
||||
@@ -584,6 +584,9 @@ class DeoptRetAddressInstr : public DeoptInstr {
|
||||
if (!ic_data.IsNull()) {
|
||||
ic_data.set_deopt_reason(deopt_context->deopt_reason());
|
||||
}
|
||||
} else if (deopt_context->deopt_reason() == kDeoptHoistedCheckClass) {
|
||||
// Prevent excessive deoptimization.
|
||||
Function::Handle(code.function()).set_allows_hoisting_check_class(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace dart {
|
||||
|
||||
DEFINE_FLAG(bool, array_bounds_check_elimination, true,
|
||||
"Eliminate redundant bounds checks.");
|
||||
DEFINE_FLAG(int, getter_setter_ratio, 13,
|
||||
"Ratio of getter/setter usage used for double field unboxing heuristics");
|
||||
DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination.");
|
||||
DEFINE_FLAG(int, max_polymorphic_checks, 4,
|
||||
"Maximum number of polymorphic check, otherwise it is megamorphic.");
|
||||
@@ -32,15 +34,13 @@ DEFINE_FLAG(int, max_equality_polymorphic_checks, 32,
|
||||
DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis.");
|
||||
DEFINE_FLAG(bool, trace_constant_propagation, false,
|
||||
"Print constant propagation and useless code elimination.");
|
||||
DEFINE_FLAG(bool, trace_load_optimization, false,
|
||||
"Print live sets for load optimization pass.");
|
||||
DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details.");
|
||||
DEFINE_FLAG(bool, trace_range_analysis, false, "Trace range analysis progress");
|
||||
DEFINE_FLAG(bool, truncating_left_shift, true,
|
||||
"Optimize left shift to truncate if possible");
|
||||
DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis.");
|
||||
DEFINE_FLAG(bool, trace_load_optimization, false,
|
||||
"Print live sets for load optimization pass.");
|
||||
DEFINE_FLAG(int, getter_setter_ratio, 13,
|
||||
"Ratio of getter/setter usage used for double field unboxing heuristics");
|
||||
DECLARE_FLAG(bool, eliminate_type_checks);
|
||||
DECLARE_FLAG(bool, enable_type_checks);
|
||||
DECLARE_FLAG(bool, trace_type_check_elimination);
|
||||
@@ -4493,6 +4493,9 @@ LICM::LICM(FlowGraph* flow_graph) : flow_graph_(flow_graph) {
|
||||
void LICM::Hoist(ForwardInstructionIterator* it,
|
||||
BlockEntryInstr* pre_header,
|
||||
Instruction* current) {
|
||||
if (current->IsCheckClass()) {
|
||||
current->AsCheckClass()->set_licm_hoisted(true);
|
||||
}
|
||||
// TODO(fschneider): Avoid repeated deoptimization when
|
||||
// speculatively hoisting checks.
|
||||
if (FLAG_trace_optimization) {
|
||||
@@ -4580,6 +4583,12 @@ static bool IsLoopInvariantLoad(ZoneGrowableArray<BitVector*>* sets,
|
||||
|
||||
|
||||
void LICM::Optimize() {
|
||||
if (!flow_graph()->parsed_function().function().
|
||||
allows_hoisting_check_class()) {
|
||||
// Do not hoist any.
|
||||
return;
|
||||
}
|
||||
|
||||
const ZoneGrowableArray<BlockEntryInstr*>& loop_headers =
|
||||
flow_graph()->loop_headers();
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ bool Value::Equals(Value* other) const {
|
||||
CheckClassInstr::CheckClassInstr(Value* value,
|
||||
intptr_t deopt_id,
|
||||
const ICData& unary_checks)
|
||||
: unary_checks_(unary_checks) {
|
||||
: unary_checks_(unary_checks), licm_hoisted_(false) {
|
||||
ASSERT(unary_checks.IsZoneHandle());
|
||||
// Expected useful check data.
|
||||
ASSERT(!unary_checks_.IsNull());
|
||||
|
||||
@@ -6793,8 +6793,11 @@ class CheckClassInstr : public TemplateInstruction<1> {
|
||||
|
||||
virtual bool MayThrow() const { return false; }
|
||||
|
||||
void set_licm_hoisted(bool value) { licm_hoisted_ = value; }
|
||||
|
||||
private:
|
||||
const ICData& unary_checks_;
|
||||
bool licm_hoisted_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CheckClassInstr);
|
||||
};
|
||||
|
||||
@@ -4546,9 +4546,10 @@ LocationSummary* CheckClassInstr::MakeLocationSummary(bool opt) const {
|
||||
|
||||
|
||||
void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const DeoptReasonId deopt_reason =
|
||||
licm_hoisted_ ? kDeoptHoistedCheckClass : kDeoptCheckClass;
|
||||
if (IsNullCheck()) {
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
__ CompareImmediate(locs()->in(0).reg(),
|
||||
reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ b(deopt, EQ);
|
||||
@@ -4559,8 +4560,7 @@ void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
(unary_checks().NumberOfChecks() > 1));
|
||||
Register value = locs()->in(0).reg();
|
||||
Register temp = locs()->temp(0).reg();
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
Label is_ok;
|
||||
intptr_t cix = 0;
|
||||
if (unary_checks().GetReceiverClassIdAt(cix) == kSmiCid) {
|
||||
|
||||
@@ -4518,9 +4518,10 @@ LocationSummary* CheckClassInstr::MakeLocationSummary(bool opt) const {
|
||||
|
||||
|
||||
void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const DeoptReasonId deopt_reason =
|
||||
licm_hoisted_ ? kDeoptHoistedCheckClass : kDeoptCheckClass;
|
||||
if (IsNullCheck()) {
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
const Immediate& raw_null =
|
||||
Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ cmpl(locs()->in(0).reg(), raw_null);
|
||||
@@ -4532,8 +4533,7 @@ void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
(unary_checks().NumberOfChecks() > 1));
|
||||
Register value = locs()->in(0).reg();
|
||||
Register temp = locs()->temp(0).reg();
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
Label is_ok;
|
||||
intptr_t cix = 0;
|
||||
if (unary_checks().GetReceiverClassIdAt(cix) == kSmiCid) {
|
||||
|
||||
@@ -3806,9 +3806,10 @@ LocationSummary* CheckClassInstr::MakeLocationSummary(bool opt) const {
|
||||
|
||||
|
||||
void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const DeoptReasonId deopt_reason =
|
||||
licm_hoisted_ ? kDeoptHoistedCheckClass : kDeoptCheckClass;
|
||||
if (IsNullCheck()) {
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
__ BranchEqual(locs()->in(0).reg(),
|
||||
reinterpret_cast<int32_t>(Object::null()), deopt);
|
||||
return;
|
||||
@@ -3818,8 +3819,7 @@ void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
(unary_checks().NumberOfChecks() > 1));
|
||||
Register value = locs()->in(0).reg();
|
||||
Register temp = locs()->temp(0).reg();
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
Label is_ok;
|
||||
intptr_t cix = 0;
|
||||
if (unary_checks().GetReceiverClassIdAt(cix) == kSmiCid) {
|
||||
|
||||
@@ -4587,9 +4587,10 @@ LocationSummary* CheckClassInstr::MakeLocationSummary(bool opt) const {
|
||||
|
||||
|
||||
void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const DeoptReasonId deopt_reason =
|
||||
licm_hoisted_ ? kDeoptHoistedCheckClass : kDeoptCheckClass;
|
||||
if (IsNullCheck()) {
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
__ CompareObject(locs()->in(0).reg(),
|
||||
Object::null_object(), PP);
|
||||
__ j(EQUAL, deopt);
|
||||
@@ -4600,8 +4601,7 @@ void CheckClassInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
(unary_checks().NumberOfChecks() > 1));
|
||||
Register value = locs()->in(0).reg();
|
||||
Register temp = locs()->temp(0).reg();
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(),
|
||||
kDeoptCheckClass);
|
||||
Label* deopt = compiler->AddDeoptStub(deopt_id(), deopt_reason);
|
||||
Label is_ok;
|
||||
intptr_t cix = 0;
|
||||
if (unary_checks().GetReceiverClassIdAt(cix) == kSmiCid) {
|
||||
|
||||
@@ -5007,11 +5007,18 @@ void Function::SetIsNativeAutoSetupScope(bool value) const {
|
||||
set_is_optimizable(value);
|
||||
}
|
||||
|
||||
|
||||
void Function::set_is_optimizable(bool value) const {
|
||||
set_kind_tag(OptimizableBit::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
|
||||
|
||||
void Function::set_allows_hoisting_check_class(bool value) const {
|
||||
set_kind_tag(
|
||||
AllowsHoistingCheckClassBit::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
|
||||
|
||||
void Function::set_is_native(bool value) const {
|
||||
set_kind_tag(NativeBit::update(value, raw_ptr()->kind_tag_));
|
||||
}
|
||||
@@ -5525,6 +5532,7 @@ RawFunction* Function::New(const String& name,
|
||||
result.set_optimized_call_site_count(0);
|
||||
result.set_is_optimizable(is_native ? false : true);
|
||||
result.set_is_inlinable(true);
|
||||
result.set_allows_hoisting_check_class(true);
|
||||
if (kind == RawFunction::kClosureFunction) {
|
||||
const ClosureData& data = ClosureData::Handle(ClosureData::New());
|
||||
result.set_data(data);
|
||||
|
||||
@@ -1722,6 +1722,11 @@ class Function : public Object {
|
||||
}
|
||||
void set_is_redirecting(bool value) const;
|
||||
|
||||
bool allows_hoisting_check_class() const {
|
||||
return AllowsHoistingCheckClassBit::decode(raw_ptr()->kind_tag_);
|
||||
}
|
||||
void set_allows_hoisting_check_class(bool value) const;
|
||||
|
||||
bool HasOptimizedCode() const;
|
||||
|
||||
// Returns true if the argument counts are valid for calling this function.
|
||||
@@ -1892,6 +1897,7 @@ class Function : public Object {
|
||||
kNativeBit = 12,
|
||||
kRedirectingBit = 13,
|
||||
kExternalBit = 14,
|
||||
kAllowsHoistingCheckClassBit = 15,
|
||||
};
|
||||
class KindBits :
|
||||
public BitField<RawFunction::Kind, kKindTagBit, kKindTagSize> {}; // NOLINT
|
||||
@@ -1906,6 +1912,8 @@ class Function : public Object {
|
||||
class NativeBit : public BitField<bool, kNativeBit, 1> {};
|
||||
class ExternalBit : public BitField<bool, kExternalBit, 1> {};
|
||||
class RedirectingBit : public BitField<bool, kRedirectingBit, 1> {};
|
||||
class AllowsHoistingCheckClassBit :
|
||||
public BitField<bool, kAllowsHoistingCheckClassBit, 1> {}; // NOLINT
|
||||
|
||||
void set_name(const String& value) const;
|
||||
void set_kind(RawFunction::Kind value) const;
|
||||
|
||||
Reference in New Issue
Block a user