diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h index eaa419e5874..47c2d585284 100644 --- a/runtime/vm/code_generator.h +++ b/runtime/vm/code_generator.h @@ -71,6 +71,7 @@ DECLARE_RUNTIME_ENTRY(UpdateFieldCid); V(UnaryOp) \ V(UnboxInteger) \ V(CheckClass) \ + V(HoistedCheckClass) \ V(CheckSmi) \ V(CheckArrayBound) \ V(AtCall) \ diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc index e8a5b4f91e0..6f65346a5ab 100644 --- a/runtime/vm/compiler.cc +++ b/runtime/vm/compiler.cc @@ -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()); diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc index 874c314821d..150e19142dc 100644 --- a/runtime/vm/deopt_instructions.cc +++ b/runtime/vm/deopt_instructions.cc @@ -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); } } diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc index 6f3c45648ea..d2291d64db1 100644 --- a/runtime/vm/flow_graph_optimizer.cc +++ b/runtime/vm/flow_graph_optimizer.cc @@ -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* sets, void LICM::Optimize() { + if (!flow_graph()->parsed_function().function(). + allows_hoisting_check_class()) { + // Do not hoist any. + return; + } + const ZoneGrowableArray& loop_headers = flow_graph()->loop_headers(); diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc index 1c79445b45a..9a06cfd1230 100644 --- a/runtime/vm/intermediate_language.cc +++ b/runtime/vm/intermediate_language.cc @@ -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()); diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h index 825806ca39f..17272be4aa2 100644 --- a/runtime/vm/intermediate_language.h +++ b/runtime/vm/intermediate_language.h @@ -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); }; diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc index b15d29fd0ef..55c8e2cf4b6 100644 --- a/runtime/vm/intermediate_language_arm.cc +++ b/runtime/vm/intermediate_language_arm.cc @@ -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(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) { diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc index d482020a87e..be9f97fce2f 100644 --- a/runtime/vm/intermediate_language_ia32.cc +++ b/runtime/vm/intermediate_language_ia32.cc @@ -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(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) { diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc index 34be46a3ac1..df916f901ee 100644 --- a/runtime/vm/intermediate_language_mips.cc +++ b/runtime/vm/intermediate_language_mips.cc @@ -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(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) { diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc index 26c84a50d6a..ecb7ec4e2fa 100644 --- a/runtime/vm/intermediate_language_x64.cc +++ b/runtime/vm/intermediate_language_x64.cc @@ -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) { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index a30bfda37c4..7916b384b15 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -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); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 85cccf1370b..dda02bb64c0 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -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 {}; // NOLINT @@ -1906,6 +1912,8 @@ class Function : public Object { class NativeBit : public BitField {}; class ExternalBit : public BitField {}; class RedirectingBit : public BitField {}; + class AllowsHoistingCheckClassBit : + public BitField {}; // NOLINT void set_name(const String& value) const; void set_kind(RawFunction::Kind value) const;