Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
47
48void JitCallSpecializer::ReplaceWithStaticCall(InstanceCallInstr* instr,
49 const Function& target,
50 intptr_t call_count) {
51 StaticCallInstr* call =
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 }
60 instr->ReplaceWith(call, current_iterator());
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 = !target.is_polymorphic_target();
118 }
119 }
120
121 if (has_one_target) {
122 const Function& target = targets.FirstTarget();
123 if (flow_graph()->CheckForInstanceCall(instr, target.kind()) ==
125 ReplaceWithStaticCall(instr, target, targets.AggregateCallCount());
126 return;
127 }
128 }
129
130 // If there is only one target we can make this into a deopting class check,
131 // followed by a call instruction that does not check the class of the
132 // receiver. This enables a lot of optimizations because after the class
133 // check we can probably inline the call and not worry about side effects.
134 // However, this can fall down if new receiver classes arrive at this call
135 // site after we generated optimized code. This causes a deopt, and after a
136 // few deopts we won't optimize this function any more at all. Therefore for
137 // very polymorphic sites we don't make this optimization, keeping it as a
138 // regular checked PolymorphicInstanceCall, which falls back to the slow but
139 // non-deopting megamorphic call stub when it sees new receiver classes.
140 if (has_one_target && FLAG_polymorphic_with_deopt &&
141 (!instr->ic_data()->HasDeoptReason(ICData::kDeoptCheckClass) ||
142 targets.length() <= FLAG_max_polymorphic_checks)) {
143 // Type propagation has not run yet, we cannot eliminate the check.
144 AddReceiverCheck(instr);
145
146 // Call can still deoptimize, do not detach environment from instr.
147 const Function& target = targets.FirstTarget();
148 ReplaceWithStaticCall(instr, target, targets.AggregateCallCount());
149 } else {
152 /* complete = */ false);
153 instr->ReplaceWith(call, current_iterator());
154 }
155}
156
157// Replace generic context allocation or cloning with a sequence of inlined
158// allocation and explicit initializing stores.
159// If context_value is not nullptr then newly allocated context is a populated
160// with values copied from it, otherwise it is initialized with null.
161void JitCallSpecializer::LowerContextAllocation(
162 Definition* alloc,
163 const ZoneGrowableArray<const Slot*>& context_variables,
164 Value* context_value) {
165 ASSERT(alloc->IsAllocateContext() || alloc->IsCloneContext());
166
169 alloc->source(), context_variables.length(), alloc->deopt_id());
170 alloc->ReplaceWith(replacement, current_iterator());
171
172 Instruction* cursor = replacement;
173
174 Value* initial_value;
175 if (context_value != nullptr) {
177 new (Z) LoadFieldInstr(context_value->CopyWithType(Z),
178 Slot::Context_parent(), alloc->source());
179 flow_graph()->InsertAfter(cursor, load, nullptr, FlowGraph::kValue);
180 cursor = load;
181 initial_value = new (Z) Value(load);
182 } else {
183 initial_value = new (Z) Value(flow_graph()->constant_null());
184 }
185 StoreFieldInstr* store = new (Z) StoreFieldInstr(
186 Slot::Context_parent(), new (Z) Value(replacement), initial_value,
188 flow_graph()->InsertAfter(cursor, store, nullptr, FlowGraph::kEffect);
189 cursor = replacement;
190
191 for (auto& slot : context_variables) {
192 if (context_value != nullptr) {
193 LoadFieldInstr* load = new (Z) LoadFieldInstr(
194 context_value->CopyWithType(Z), *slot, alloc->source());
195 flow_graph()->InsertAfter(cursor, load, nullptr, FlowGraph::kValue);
196 cursor = load;
197 initial_value = new (Z) Value(load);
198 } else {
199 initial_value = new (Z) Value(flow_graph()->constant_null());
200 }
201
202 store = new (Z) StoreFieldInstr(
203 *slot, new (Z) Value(replacement), initial_value, kNoStoreBarrier,
205 flow_graph()->InsertAfter(cursor, store, nullptr, FlowGraph::kEffect);
206 cursor = store;
207 }
208}
209
211 LowerContextAllocation(instr, instr->context_slots(), nullptr);
212}
213
215 LowerContextAllocation(instr, instr->context_slots(), instr->context_value());
216}
217
218} // namespace dart
SI void store(P *ptr, const T &val)
SI T load(const P *ptr)
#define Z
const ZoneGrowableArray< const Slot * > & context_slots() const
Definition il.h:8340
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:5507
intptr_t AggregateCallCount() const
Definition il.cc:5530
const Function & FirstTarget() const
Definition il.cc:5515
StaticTypeExactnessState MonomorphicExactness() const
Definition il.cc:809
intptr_t MonomorphicReceiverCid() const
Definition il.cc:804
intptr_t length() const
Definition il.h:752
bool is_empty() const
Definition il.h:756
bool IsMonomorphic() const
Definition il.cc:799
const ZoneGrowableArray< const Slot * > & context_slots() const
Definition il.h:8387
Value * context_value() const
Definition il.h:8385
void ReplaceWith(Definition *other, ForwardInstructionIterator *iterator)
Definition il.cc:1653
ForwardInstructionIterator * current_iterator() const
Definition il.h:11792
void AddExactnessGuard(InstanceCallInstr *call, intptr_t receiver_cid)
ConstantInstr * constant_null() const
Definition flow_graph.h:270
void InsertAfter(Instruction *prev, Instruction *instr, Environment *env, UseKind use_kind)
bool HasDeoptReason(ICData::DeoptReasonId reason) const
Definition object.cc:16560
const ICData * ic_data() const
Definition il.h:4698
Token::Kind token_kind() const
Definition il.h:4707
const CallTargets & Targets()
Definition il.cc:5347
InstructionSource source() const
Definition il.h:1002
intptr_t deopt_id() const
Definition il.h:987
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:5570
static PolymorphicInstanceCallInstr * FromCall(Zone *zone, InstanceCallBaseInstr *call, const CallTargets &targets, bool complete)
Definition il.h:4906
static StaticCallInstr * FromCall(Zone *zone, const C *call, const Function &target, intptr_t call_count)
Definition il.h:5535
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
Value * CopyWithType(Zone *zone)
Definition il.h:138
#define ASSERT(E)
uint32_t * target
@ kNoStoreBarrier
Definition il.h:6252