Flutter Engine
The Flutter Engine
jit_call_specializer.cc
Go to the documentation of this file.
1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
5
6#include "vm/bit_vector.h"
13#include "vm/compiler/cha.h"
16#include "vm/cpu.h"
17#include "vm/dart_entry.h"
18#include "vm/exceptions.h"
19#include "vm/hash_map.h"
20#include "vm/object_store.h"
21#include "vm/parser.h"
22#include "vm/resolver.h"
23#include "vm/scopes.h"
24#include "vm/stack_frame.h"
25#include "vm/symbols.h"
26
27namespace dart {
28
29// Quick access to the current zone.
30#define Z (zone())
31
33 FlowGraph* flow_graph,
34 SpeculativeInliningPolicy* speculative_policy)
35 : CallSpecializer(flow_graph,
36 speculative_policy,
37 CompilerState::Current().should_clone_fields()) {}
38
39bool JitCallSpecializer::IsAllowedForInlining(intptr_t deopt_id) const {
40 return true;
41}
42
45 return false;
46}
47
48void JitCallSpecializer::ReplaceWithStaticCall(InstanceCallInstr* instr,
49 const Function& target,
50 intptr_t call_count) {
52 StaticCallInstr::FromCall(Z, instr, target, call_count);
53 const CallTargets& targets = instr->Targets();
54 if (targets.IsMonomorphic() && targets.MonomorphicExactness().IsExact()) {
55 if (targets.MonomorphicExactness().IsTriviallyExact()) {
57 }
58 call->set_entry_kind(Code::EntryKind::kUnchecked);
59 }
61}
62
63// Tries to optimize instance call by replacing it with a faster instruction
64// (e.g, binary op, field load, ..).
65// TODO(dartbug.com/30635) Evaluate how much this can be shared with
66// AotCallSpecializer.
68 const CallTargets& targets = instr->Targets();
69 if (targets.is_empty()) {
70 return; // No feedback.
71 }
72
73 const Token::Kind op_kind = instr->token_kind();
74
75 // Type test is special as it always gets converted into inlined code.
76 if (Token::IsTypeTestOperator(op_kind)) {
78 return;
79 }
80
81 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) {
82 return;
83 }
84
85 if (Token::IsRelationalOperator(op_kind) &&
86 TryReplaceWithRelationalOp(instr, op_kind)) {
87 return;
88 }
89
90 if (Token::IsBinaryOperator(op_kind) &&
91 TryReplaceWithBinaryOp(instr, op_kind)) {
92 return;
93 }
94 if (Token::IsUnaryOperator(op_kind) &&
95 TryReplaceWithUnaryOp(instr, op_kind)) {
96 return;
97 }
98 if ((op_kind == Token::kGET) && TryInlineInstanceGetter(instr)) {
99 return;
100 }
101 if ((op_kind == Token::kSET) && TryInlineInstanceSetter(instr)) {
102 return;
103 }
104 if (TryInlineInstanceMethod(instr)) {
105 return;
106 }
107
108 bool has_one_target = targets.HasSingleTarget();
109 if (has_one_target) {
110 // Check if the single target is a polymorphic target, if it is,
111 // we don't have one target.
112 const Function& target = targets.FirstTarget();
113 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) {
115 targets) != Type::null();
116 } else {
117 has_one_target =
118 !target.is_polymorphic_target() && !target.IsDynamicallyOverridden();
119 }
120 }
121
122 if (has_one_target) {
123 const Function& target = targets.FirstTarget();
124 if (flow_graph()->CheckForInstanceCall(instr, target.kind()) ==
126 ReplaceWithStaticCall(instr, target, targets.AggregateCallCount());
127 return;
128 }
129 }
130
131 // If there is only one target we can make this into a deopting class check,
132 // followed by a call instruction that does not check the class of the
133 // receiver. This enables a lot of optimizations because after the class
134 // check we can probably inline the call and not worry about side effects.
135 // However, this can fall down if new receiver classes arrive at this call
136 // site after we generated optimized code. This causes a deopt, and after a
137 // few deopts we won't optimize this function any more at all. Therefore for
138 // very polymorphic sites we don't make this optimization, keeping it as a
139 // regular checked PolymorphicInstanceCall, which falls back to the slow but
140 // non-deopting megamorphic call stub when it sees new receiver classes.
141 if (has_one_target && FLAG_polymorphic_with_deopt &&
142 (!instr->ic_data()->HasDeoptReason(ICData::kDeoptCheckClass) ||
143 targets.length() <= FLAG_max_polymorphic_checks)) {
144 // Type propagation has not run yet, we cannot eliminate the check.
145 AddReceiverCheck(instr);
146
147 // Call can still deoptimize, do not detach environment from instr.
148 const Function& target = targets.FirstTarget();
149 ReplaceWithStaticCall(instr, target, targets.AggregateCallCount());
150 } else {
153 /* complete = */ false);
155 }
156}
157
158// Replace generic context allocation or cloning with a sequence of inlined
159// allocation and explicit initializing stores.
160// If context_value is not nullptr then newly allocated context is a populated
161// with values copied from it, otherwise it is initialized with null.
162void JitCallSpecializer::LowerContextAllocation(
163 Definition* alloc,
164 const ZoneGrowableArray<const Slot*>& context_variables,
165 Value* context_value) {
166 ASSERT(alloc->IsAllocateContext() || alloc->IsCloneContext());
167
170 alloc->source(), context_variables.length(), alloc->deopt_id());
171 alloc->ReplaceWith(replacement, current_iterator());
172
173 Instruction* cursor = replacement;
174
175 Value* initial_value;
176 if (context_value != nullptr) {
178 new (Z) LoadFieldInstr(context_value->CopyWithType(Z),
179 Slot::Context_parent(), alloc->source());
180 flow_graph()->InsertAfter(cursor, load, nullptr, FlowGraph::kValue);
181 cursor = load;
182 initial_value = new (Z) Value(load);
183 } else {
184 initial_value = new (Z) Value(flow_graph()->constant_null());
185 }
186 StoreFieldInstr* store = new (Z) StoreFieldInstr(
187 Slot::Context_parent(), new (Z) Value(replacement), initial_value,
189 flow_graph()->InsertAfter(cursor, store, nullptr, FlowGraph::kEffect);
190 cursor = replacement;
191
192 for (auto& slot : context_variables) {
193 if (context_value != nullptr) {
194 LoadFieldInstr* load = new (Z) LoadFieldInstr(
195 context_value->CopyWithType(Z), *slot, alloc->source());
196 flow_graph()->InsertAfter(cursor, load, nullptr, FlowGraph::kValue);
197 cursor = load;
198 initial_value = new (Z) Value(load);
199 } else {
200 initial_value = new (Z) Value(flow_graph()->constant_null());
201 }
202
203 store = new (Z) StoreFieldInstr(
204 *slot, new (Z) Value(replacement), initial_value, kNoStoreBarrier,
206 flow_graph()->InsertAfter(cursor, store, nullptr, FlowGraph::kEffect);
207 cursor = store;
208 }
209}
210
212 LowerContextAllocation(instr, instr->context_slots(), nullptr);
213}
214
216 LowerContextAllocation(instr, instr->context_slots(), instr->context_value());
217}
218
219} // namespace dart
SI void store(P *ptr, const T &val)
SI T load(const P *ptr)
Definition: Transform_inl.h:98
const ZoneGrowableArray< const Slot * > & context_slots() const
Definition: il.h:8388
intptr_t length() const
void AddReceiverCheck(InstanceCallInstr *call)
bool TryReplaceWithEqualityOp(InstanceCallInstr *call, Token::Kind op_kind)
FlowGraph * flow_graph() const
bool TryInlineInstanceSetter(InstanceCallInstr *call)
bool TryInlineInstanceGetter(InstanceCallInstr *call)
bool TryReplaceWithRelationalOp(InstanceCallInstr *call, Token::Kind op_kind)
bool TryReplaceWithUnaryOp(InstanceCallInstr *call, Token::Kind op_kind)
void ReplaceWithInstanceOf(InstanceCallInstr *instr)
bool TryReplaceWithBinaryOp(InstanceCallInstr *call, Token::Kind op_kind)
bool TryInlineInstanceMethod(InstanceCallInstr *call)
bool HasSingleTarget() const
Definition: il.cc:5505
intptr_t AggregateCallCount() const
Definition: il.cc:5528
const Function & FirstTarget() const
Definition: il.cc:5513
StaticTypeExactnessState MonomorphicExactness() const
Definition: il.cc:811
intptr_t MonomorphicReceiverCid() const
Definition: il.cc:806
intptr_t length() const
Definition: il.h:758
bool is_empty() const
Definition: il.h:762
bool IsMonomorphic() const
Definition: il.cc:801
const ZoneGrowableArray< const Slot * > & context_slots() const
Definition: il.h:8435
Value * context_value() const
Definition: il.h:8433
void ReplaceWith(Definition *other, ForwardInstructionIterator *iterator)
Definition: il.cc:1662
ForwardInstructionIterator * current_iterator() const
Definition: il.h:11846
void AddExactnessGuard(InstanceCallInstr *call, intptr_t receiver_cid)
Definition: flow_graph.cc:627
ConstantInstr * constant_null() const
Definition: flow_graph.h:270
void InsertAfter(Instruction *prev, Instruction *instr, Environment *env, UseKind use_kind)
Definition: flow_graph.cc:273
bool HasDeoptReason(ICData::DeoptReasonId reason) const
Definition: object.cc:16513
const ICData * ic_data() const
Definition: il.h:4716
Token::Kind token_kind() const
Definition: il.h:4725
const CallTargets & Targets()
Definition: il.cc:5345
InstructionSource source() const
Definition: il.h:1008
intptr_t deopt_id() const
Definition: il.h:993
virtual void VisitInstanceCall(InstanceCallInstr *instr)
JitCallSpecializer(FlowGraph *flow_graph, SpeculativeInliningPolicy *speculative_policy)
virtual bool IsAllowedForInlining(intptr_t deopt_id) const
virtual void VisitCloneContext(CloneContextInstr *instr)
virtual bool TryOptimizeStaticCallUsingStaticTypes(StaticCallInstr *call)
virtual void VisitAllocateContext(AllocateContextInstr *instr)
static ObjectPtr null()
Definition: object.h:433
static TypePtr ComputeRuntimeType(const CallTargets &targets)
Definition: il.cc:5568
static PolymorphicInstanceCallInstr * FromCall(Zone *zone, InstanceCallBaseInstr *call, const CallTargets &targets, bool complete)
Definition: il.h:4924
static StaticCallInstr * FromCall(Zone *zone, const C *call, const Function &target, intptr_t call_count)
Definition: il.h:5584
static bool IsTypeTestOperator(Kind tok)
Definition: token.h:244
static bool IsRelationalOperator(Kind tok)
Definition: token.h:232
static bool IsBinaryOperator(Token::Kind token)
Definition: token.cc:31
static bool IsUnaryOperator(Token::Kind token)
Definition: token.cc:41
Definition: il.h:75
Value * CopyWithType(Zone *zone)
Definition: il.h:138
#define ASSERT(E)
uint32_t * target
#define Z
Definition: dart_vm.cc:33
@ kNoStoreBarrier
Definition: il.h:6301
def call(args)
Definition: dom.py:159