OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/aot_optimizer.h" |
6 | 6 |
7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
11 #include "vm/cpu.h" | 11 #include "vm/cpu.h" |
12 #include "vm/dart_entry.h" | 12 #include "vm/dart_entry.h" |
13 #include "vm/exceptions.h" | 13 #include "vm/exceptions.h" |
14 #include "vm/flow_graph_builder.h" | 14 #include "vm/flow_graph_builder.h" |
15 #include "vm/flow_graph_compiler.h" | 15 #include "vm/flow_graph_compiler.h" |
16 #include "vm/flow_graph_inliner.h" | 16 #include "vm/flow_graph_inliner.h" |
17 #include "vm/flow_graph_range_analysis.h" | 17 #include "vm/flow_graph_range_analysis.h" |
18 #include "vm/hash_map.h" | 18 #include "vm/hash_map.h" |
19 #include "vm/il_printer.h" | 19 #include "vm/il_printer.h" |
20 #include "vm/intermediate_language.h" | 20 #include "vm/intermediate_language.h" |
21 #include "vm/object.h" | |
21 #include "vm/object_store.h" | 22 #include "vm/object_store.h" |
22 #include "vm/parser.h" | 23 #include "vm/parser.h" |
23 #include "vm/precompiler.h" | 24 #include "vm/precompiler.h" |
24 #include "vm/resolver.h" | 25 #include "vm/resolver.h" |
25 #include "vm/scopes.h" | 26 #include "vm/scopes.h" |
26 #include "vm/stack_frame.h" | 27 #include "vm/stack_frame.h" |
27 #include "vm/symbols.h" | 28 #include "vm/symbols.h" |
28 | 29 |
29 namespace dart { | 30 namespace dart { |
30 | 31 |
31 DEFINE_FLAG(int, getter_setter_ratio, 13, | 32 DECLARE_FLAG(bool, guess_icdata_cid); |
Ivan Posva
2016/02/19 01:07:49
Please move these to flag_list.h so that we keep r
Florian Schneider
2016/02/19 17:42:29
Done.
| |
32 "Ratio of getter/setter usage used for double field unboxing heuristics"); | 33 DECLARE_FLAG(int, max_polymorphic_checks); |
33 DEFINE_FLAG(bool, guess_icdata_cid, true, | 34 DECLARE_FLAG(int, max_equality_polymorphic_checks); |
34 "Artificially create type feedback for arithmetic etc. operations" | 35 DECLARE_FLAG(bool, merge_sin_cos); |
35 " by guessing the other unknown argument cid"); | 36 DECLARE_FLAG(bool, trace_optimization); |
36 DEFINE_FLAG(int, max_polymorphic_checks, 4, | 37 DECLARE_FLAG(bool, truncating_left_shift); |
37 "Maximum number of polymorphic check, otherwise it is megamorphic."); | 38 DECLARE_FLAG(bool, use_cha_deopt); |
38 DEFINE_FLAG(int, max_equality_polymorphic_checks, 32, | |
39 "Maximum number of polymorphic checks in equality operator," | |
40 " otherwise use megamorphic dispatch."); | |
41 DEFINE_FLAG(bool, merge_sin_cos, false, "Merge sin/cos into sincos"); | |
42 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); | |
43 DEFINE_FLAG(bool, truncating_left_shift, true, | |
44 "Optimize left shift to truncate if possible"); | |
45 DEFINE_FLAG(bool, use_cha_deopt, true, | |
46 "Use class hierarchy analysis even if it can cause deoptimization."); | |
47 | |
48 DECLARE_FLAG(bool, precompilation); | 39 DECLARE_FLAG(bool, precompilation); |
49 DECLARE_FLAG(bool, polymorphic_with_deopt); | 40 DECLARE_FLAG(bool, polymorphic_with_deopt); |
50 DECLARE_FLAG(bool, trace_cha); | 41 DECLARE_FLAG(bool, trace_cha); |
51 DECLARE_FLAG(bool, trace_field_guards); | 42 DECLARE_FLAG(bool, trace_field_guards); |
52 | 43 |
53 // Quick access to the current isolate and zone. | 44 // Quick access to the current isolate and zone. |
54 #define I (isolate()) | 45 #define I (isolate()) |
55 #define Z (zone()) | 46 #define Z (zone()) |
56 | 47 |
57 static bool ShouldInlineSimd() { | 48 static bool ShouldInlineSimd() { |
58 return FlowGraphCompiler::SupportsUnboxedSimd128(); | 49 return FlowGraphCompiler::SupportsUnboxedSimd128(); |
59 } | 50 } |
60 | 51 |
61 | 52 |
62 static bool CanUnboxDouble() { | 53 static bool CanUnboxDouble() { |
63 return FlowGraphCompiler::SupportsUnboxedDoubles(); | 54 return FlowGraphCompiler::SupportsUnboxedDoubles(); |
64 } | 55 } |
65 | 56 |
66 | 57 |
67 static bool CanConvertUnboxedMintToDouble() { | 58 static bool CanConvertUnboxedMintToDouble() { |
68 return FlowGraphCompiler::CanConvertUnboxedMintToDouble(); | 59 return FlowGraphCompiler::CanConvertUnboxedMintToDouble(); |
69 } | 60 } |
70 | 61 |
71 | 62 |
72 // Optimize instance calls using ICData. | 63 // Optimize instance calls using ICData. |
73 void FlowGraphOptimizer::ApplyICData() { | 64 void AotOptimizer::ApplyICData() { |
74 VisitBlocks(); | 65 VisitBlocks(); |
75 } | 66 } |
76 | 67 |
77 | 68 |
78 void FlowGraphOptimizer::PopulateWithICData() { | 69 void AotOptimizer::PopulateWithICData() { |
79 ASSERT(current_iterator_ == NULL); | 70 ASSERT(current_iterator_ == NULL); |
80 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 71 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
81 !block_it.Done(); | 72 !block_it.Done(); |
82 block_it.Advance()) { | 73 block_it.Advance()) { |
83 ForwardInstructionIterator it(block_it.Current()); | 74 ForwardInstructionIterator it(block_it.Current()); |
84 for (; !it.Done(); it.Advance()) { | 75 for (; !it.Done(); it.Advance()) { |
85 Instruction* instr = it.Current(); | 76 Instruction* instr = it.Current(); |
86 if (instr->IsInstanceCall()) { | 77 if (instr->IsInstanceCall()) { |
87 InstanceCallInstr* call = instr->AsInstanceCall(); | 78 InstanceCallInstr* call = instr->AsInstanceCall(); |
88 if (!call->HasICData()) { | 79 if (!call->HasICData()) { |
(...skipping 13 matching lines...) Expand all Loading... | |
102 } | 93 } |
103 } | 94 } |
104 | 95 |
105 | 96 |
106 // Optimize instance calls using cid. This is called after optimizer | 97 // Optimize instance calls using cid. This is called after optimizer |
107 // converted instance calls to instructions. Any remaining | 98 // converted instance calls to instructions. Any remaining |
108 // instance calls are either megamorphic calls, cannot be optimized or | 99 // instance calls are either megamorphic calls, cannot be optimized or |
109 // have no runtime type feedback collected. | 100 // have no runtime type feedback collected. |
110 // Attempts to convert an instance call (IC call) using propagated class-ids, | 101 // Attempts to convert an instance call (IC call) using propagated class-ids, |
111 // e.g., receiver class id, guarded-cid, or by guessing cid-s. | 102 // e.g., receiver class id, guarded-cid, or by guessing cid-s. |
112 void FlowGraphOptimizer::ApplyClassIds() { | 103 void AotOptimizer::ApplyClassIds() { |
113 ASSERT(current_iterator_ == NULL); | 104 ASSERT(current_iterator_ == NULL); |
114 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 105 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
115 !block_it.Done(); | 106 !block_it.Done(); |
116 block_it.Advance()) { | 107 block_it.Advance()) { |
117 ForwardInstructionIterator it(block_it.Current()); | 108 ForwardInstructionIterator it(block_it.Current()); |
118 current_iterator_ = ⁢ | 109 current_iterator_ = ⁢ |
119 for (; !it.Done(); it.Advance()) { | 110 for (; !it.Done(); it.Advance()) { |
120 Instruction* instr = it.Current(); | 111 Instruction* instr = it.Current(); |
121 if (instr->IsInstanceCall()) { | 112 if (instr->IsInstanceCall()) { |
122 InstanceCallInstr* call = instr->AsInstanceCall(); | 113 InstanceCallInstr* call = instr->AsInstanceCall(); |
(...skipping 10 matching lines...) Expand all Loading... | |
133 } | 124 } |
134 } | 125 } |
135 | 126 |
136 | 127 |
137 // TODO(srdjan): Test/support other number types as well. | 128 // TODO(srdjan): Test/support other number types as well. |
138 static bool IsNumberCid(intptr_t cid) { | 129 static bool IsNumberCid(intptr_t cid) { |
139 return (cid == kSmiCid) || (cid == kDoubleCid); | 130 return (cid == kSmiCid) || (cid == kDoubleCid); |
140 } | 131 } |
141 | 132 |
142 | 133 |
143 bool FlowGraphOptimizer::TryCreateICData(InstanceCallInstr* call) { | 134 static void GetUniqueDynamicTarget(Isolate* isolate, |
135 const String& fname, | |
136 Object* function) { | |
137 UniqueFunctionsSet functions_set( | |
138 isolate->object_store()->unique_dynamic_targets()); | |
139 ASSERT(fname.IsSymbol()); | |
140 *function = functions_set.GetOrNull(fname); | |
141 ASSERT(functions_set.Release().raw() == | |
142 isolate->object_store()->unique_dynamic_targets()); | |
143 } | |
144 | |
145 | |
146 bool AotOptimizer::TryCreateICData(InstanceCallInstr* call) { | |
144 ASSERT(call->HasICData()); | 147 ASSERT(call->HasICData()); |
145 if (call->ic_data()->NumberOfUsedChecks() > 0) { | 148 if (call->ic_data()->NumberOfUsedChecks() > 0) { |
146 // This occurs when an instance call has too many checks, will be converted | 149 // This occurs when an instance call has too many checks, will be converted |
147 // to megamorphic call. | 150 // to megamorphic call. |
148 return false; | 151 return false; |
149 } | 152 } |
150 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); | 153 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); |
151 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); | 154 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); |
152 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { | 155 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { |
153 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid()); | 156 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid()); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
210 if (class_ids.length() > 1) { | 213 if (class_ids.length() > 1) { |
211 ic_data.AddCheck(class_ids, function); | 214 ic_data.AddCheck(class_ids, function); |
212 } else { | 215 } else { |
213 ASSERT(class_ids.length() == 1); | 216 ASSERT(class_ids.length() == 1); |
214 ic_data.AddReceiverCheck(class_ids[0], function); | 217 ic_data.AddReceiverCheck(class_ids[0], function); |
215 } | 218 } |
216 call->set_ic_data(&ic_data); | 219 call->set_ic_data(&ic_data); |
217 return true; | 220 return true; |
218 } | 221 } |
219 | 222 |
220 #ifdef DART_PRECOMPILER | 223 if (isolate()->object_store()->unique_dynamic_targets() != Array::null()) { |
221 if (FLAG_precompilation && | |
222 (isolate()->object_store()->unique_dynamic_targets() != Array::null())) { | |
223 // Check if the target is unique. | 224 // Check if the target is unique. |
224 Function& target_function = Function::Handle(Z); | 225 Function& target_function = Function::Handle(Z); |
225 Precompiler::GetUniqueDynamicTarget( | 226 GetUniqueDynamicTarget(isolate(), call->function_name(), &target_function); |
226 isolate(), call->function_name(), &target_function); | |
227 // Calls with named arguments must be resolved/checked at runtime. | 227 // Calls with named arguments must be resolved/checked at runtime. |
228 String& error_message = String::Handle(Z); | 228 String& error_message = String::Handle(Z); |
229 if (!target_function.IsNull() && | 229 if (!target_function.IsNull() && |
230 !target_function.HasOptionalNamedParameters() && | 230 !target_function.HasOptionalNamedParameters() && |
231 target_function.AreValidArgumentCounts(call->ArgumentCount(), 0, | 231 target_function.AreValidArgumentCounts(call->ArgumentCount(), 0, |
232 &error_message)) { | 232 &error_message)) { |
233 const intptr_t cid = Class::Handle(Z, target_function.Owner()).id(); | 233 const intptr_t cid = Class::Handle(Z, target_function.Owner()).id(); |
234 const ICData& ic_data = ICData::ZoneHandle(Z, | 234 const ICData& ic_data = ICData::ZoneHandle(Z, |
235 ICData::NewFrom(*call->ic_data(), 1)); | 235 ICData::NewFrom(*call->ic_data(), 1)); |
236 ic_data.AddReceiverCheck(cid, target_function); | 236 ic_data.AddReceiverCheck(cid, target_function); |
237 call->set_ic_data(&ic_data); | 237 call->set_ic_data(&ic_data); |
238 return true; | 238 return true; |
239 } | 239 } |
240 } | 240 } |
241 #endif | |
242 | 241 |
243 // Check if getter or setter in function's class and class is currently leaf. | 242 // Check if getter or setter in function's class and class is currently leaf. |
244 if (FLAG_guess_icdata_cid && | 243 if (FLAG_guess_icdata_cid && |
245 ((call->token_kind() == Token::kGET) || | 244 ((call->token_kind() == Token::kGET) || |
246 (call->token_kind() == Token::kSET))) { | 245 (call->token_kind() == Token::kSET))) { |
247 const Class& owner_class = Class::Handle(Z, function().Owner()); | 246 const Class& owner_class = Class::Handle(Z, function().Owner()); |
248 if (!owner_class.is_abstract() && | 247 if (!owner_class.is_abstract() && |
249 !CHA::HasSubclasses(owner_class) && | 248 !CHA::HasSubclasses(owner_class) && |
250 !CHA::IsImplemented(owner_class)) { | 249 !CHA::IsImplemented(owner_class)) { |
251 const Array& args_desc_array = Array::Handle(Z, | 250 const Array& args_desc_array = Array::Handle(Z, |
(...skipping 11 matching lines...) Expand all Loading... | |
263 call->set_ic_data(&ic_data); | 262 call->set_ic_data(&ic_data); |
264 return true; | 263 return true; |
265 } | 264 } |
266 } | 265 } |
267 } | 266 } |
268 | 267 |
269 return false; | 268 return false; |
270 } | 269 } |
271 | 270 |
272 | 271 |
273 const ICData& FlowGraphOptimizer::TrySpecializeICData(const ICData& ic_data, | 272 const ICData& AotOptimizer::TrySpecializeICData(const ICData& ic_data, |
274 intptr_t cid) { | 273 intptr_t cid) { |
275 ASSERT(ic_data.NumArgsTested() == 1); | 274 ASSERT(ic_data.NumArgsTested() == 1); |
276 | 275 |
277 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { | 276 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { |
278 return ic_data; // Nothing to do | 277 return ic_data; // Nothing to do |
279 } | 278 } |
280 | 279 |
281 const Function& function = | 280 const Function& function = |
282 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); | 281 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); |
283 // TODO(fschneider): Try looking up the function on the class if it is | 282 // TODO(fschneider): Try looking up the function on the class if it is |
284 // not found in the ICData. | 283 // not found in the ICData. |
285 if (!function.IsNull()) { | 284 if (!function.IsNull()) { |
286 const ICData& new_ic_data = ICData::ZoneHandle(Z, ICData::New( | 285 const ICData& new_ic_data = ICData::ZoneHandle(Z, ICData::New( |
287 Function::Handle(Z, ic_data.Owner()), | 286 Function::Handle(Z, ic_data.Owner()), |
288 String::Handle(Z, ic_data.target_name()), | 287 String::Handle(Z, ic_data.target_name()), |
289 Object::empty_array(), // Dummy argument descriptor. | 288 Object::empty_array(), // Dummy argument descriptor. |
290 ic_data.deopt_id(), | 289 ic_data.deopt_id(), |
291 ic_data.NumArgsTested())); | 290 ic_data.NumArgsTested())); |
292 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); | 291 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); |
293 new_ic_data.AddReceiverCheck(cid, function); | 292 new_ic_data.AddReceiverCheck(cid, function); |
294 return new_ic_data; | 293 return new_ic_data; |
295 } | 294 } |
296 | 295 |
297 return ic_data; | 296 return ic_data; |
298 } | 297 } |
299 | 298 |
300 | 299 |
301 void FlowGraphOptimizer::SpecializePolymorphicInstanceCall( | 300 void AotOptimizer::SpecializePolymorphicInstanceCall( |
302 PolymorphicInstanceCallInstr* call) { | 301 PolymorphicInstanceCallInstr* call) { |
303 if (!FLAG_polymorphic_with_deopt) { | 302 if (!FLAG_polymorphic_with_deopt) { |
304 // Specialization adds receiver checks which can lead to deoptimization. | 303 // Specialization adds receiver checks which can lead to deoptimization. |
305 return; | 304 return; |
306 } | 305 } |
307 if (!call->with_checks()) { | 306 if (!call->with_checks()) { |
308 return; // Already specialized. | 307 return; // Already specialized. |
309 } | 308 } |
310 | 309 |
311 const intptr_t receiver_cid = | 310 const intptr_t receiver_cid = |
(...skipping 28 matching lines...) Expand all Loading... | |
340 | 339 |
341 static bool IsPositiveOrZeroSmiConst(Definition* d) { | 340 static bool IsPositiveOrZeroSmiConst(Definition* d) { |
342 ConstantInstr* const_instr = d->AsConstant(); | 341 ConstantInstr* const_instr = d->AsConstant(); |
343 if ((const_instr != NULL) && (const_instr->value().IsSmi())) { | 342 if ((const_instr != NULL) && (const_instr->value().IsSmi())) { |
344 return Smi::Cast(const_instr->value()).Value() >= 0; | 343 return Smi::Cast(const_instr->value()).Value() >= 0; |
345 } | 344 } |
346 return false; | 345 return false; |
347 } | 346 } |
348 | 347 |
349 | 348 |
350 void FlowGraphOptimizer::OptimizeLeftShiftBitAndSmiOp( | 349 void AotOptimizer::OptimizeLeftShiftBitAndSmiOp( |
351 Definition* bit_and_instr, | 350 Definition* bit_and_instr, |
352 Definition* left_instr, | 351 Definition* left_instr, |
353 Definition* right_instr) { | 352 Definition* right_instr) { |
354 ASSERT(bit_and_instr != NULL); | 353 ASSERT(bit_and_instr != NULL); |
355 ASSERT((left_instr != NULL) && (right_instr != NULL)); | 354 ASSERT((left_instr != NULL) && (right_instr != NULL)); |
356 | 355 |
357 // Check for pattern, smi_shift_left must be single-use. | 356 // Check for pattern, smi_shift_left must be single-use. |
358 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr); | 357 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr); |
359 if (!is_positive_or_zero) { | 358 if (!is_positive_or_zero) { |
360 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr); | 359 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr); |
(...skipping 17 matching lines...) Expand all Loading... | |
378 BinarySmiOpInstr* smi_op = new(Z) BinarySmiOpInstr( | 377 BinarySmiOpInstr* smi_op = new(Z) BinarySmiOpInstr( |
379 Token::kBIT_AND, | 378 Token::kBIT_AND, |
380 new(Z) Value(left_instr), | 379 new(Z) Value(left_instr), |
381 new(Z) Value(right_instr), | 380 new(Z) Value(right_instr), |
382 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. | 381 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. |
383 bit_and_instr->ReplaceWith(smi_op, current_iterator()); | 382 bit_and_instr->ReplaceWith(smi_op, current_iterator()); |
384 } | 383 } |
385 } | 384 } |
386 | 385 |
387 | 386 |
388 void FlowGraphOptimizer::AppendExtractNthOutputForMerged(Definition* instr, | 387 void AotOptimizer::AppendExtractNthOutputForMerged(Definition* instr, |
389 intptr_t index, | 388 intptr_t index, |
390 Representation rep, | 389 Representation rep, |
391 intptr_t cid) { | 390 intptr_t cid) { |
392 ExtractNthOutputInstr* extract = | 391 ExtractNthOutputInstr* extract = |
393 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); | 392 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); |
394 instr->ReplaceUsesWith(extract); | 393 instr->ReplaceUsesWith(extract); |
395 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); | 394 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); |
396 } | 395 } |
397 | 396 |
398 | 397 |
399 // Dart: | 398 // Dart: |
400 // var x = d % 10; | 399 // var x = d % 10; |
401 // var y = d ~/ 10; | 400 // var y = d ~/ 10; |
402 // var z = x + y; | 401 // var z = x + y; |
403 // | 402 // |
404 // IL: | 403 // IL: |
405 // v4 <- %(v2, v3) | 404 // v4 <- %(v2, v3) |
406 // v5 <- ~/(v2, v3) | 405 // v5 <- ~/(v2, v3) |
407 // v6 <- +(v4, v5) | 406 // v6 <- +(v4, v5) |
408 // | 407 // |
409 // IL optimized: | 408 // IL optimized: |
410 // v4 <- DIVMOD(v2, v3); | 409 // v4 <- DIVMOD(v2, v3); |
411 // v5 <- LoadIndexed(v4, 0); // ~/ result | 410 // v5 <- LoadIndexed(v4, 0); // ~/ result |
412 // v6 <- LoadIndexed(v4, 1); // % result | 411 // v6 <- LoadIndexed(v4, 1); // % result |
413 // v7 <- +(v5, v6) | 412 // v7 <- +(v5, v6) |
414 // Because of the environment it is important that merged instruction replaces | 413 // Because of the environment it is important that merged instruction replaces |
415 // first original instruction encountered. | 414 // first original instruction encountered. |
416 void FlowGraphOptimizer::TryMergeTruncDivMod( | 415 void AotOptimizer::TryMergeTruncDivMod( |
417 GrowableArray<BinarySmiOpInstr*>* merge_candidates) { | 416 GrowableArray<BinarySmiOpInstr*>* merge_candidates) { |
418 if (merge_candidates->length() < 2) { | 417 if (merge_candidates->length() < 2) { |
419 // Need at least a TRUNCDIV and a MOD. | 418 // Need at least a TRUNCDIV and a MOD. |
420 return; | 419 return; |
421 } | 420 } |
422 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | 421 for (intptr_t i = 0; i < merge_candidates->length(); i++) { |
423 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i]; | 422 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i]; |
424 if (curr_instr == NULL) { | 423 if (curr_instr == NULL) { |
425 // Instruction was merged already. | 424 // Instruction was merged already. |
426 continue; | 425 continue; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
467 // more candidates are possible. | 466 // more candidates are possible. |
468 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod. | 467 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod. |
469 break; | 468 break; |
470 } | 469 } |
471 } | 470 } |
472 } | 471 } |
473 } | 472 } |
474 | 473 |
475 | 474 |
476 // Tries to merge MathUnary operations, in this case sinus and cosinus. | 475 // Tries to merge MathUnary operations, in this case sinus and cosinus. |
477 void FlowGraphOptimizer::TryMergeMathUnary( | 476 void AotOptimizer::TryMergeMathUnary( |
478 GrowableArray<MathUnaryInstr*>* merge_candidates) { | 477 GrowableArray<MathUnaryInstr*>* merge_candidates) { |
479 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble() || | 478 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble() || |
480 !FLAG_merge_sin_cos) { | 479 !FLAG_merge_sin_cos) { |
481 return; | 480 return; |
482 } | 481 } |
483 if (merge_candidates->length() < 2) { | 482 if (merge_candidates->length() < 2) { |
484 // Need at least a SIN and a COS. | 483 // Need at least a SIN and a COS. |
485 return; | 484 return; |
486 } | 485 } |
487 for (intptr_t i = 0; i < merge_candidates->length(); i++) { | 486 for (intptr_t i = 0; i < merge_candidates->length(); i++) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
528 break; | 527 break; |
529 } | 528 } |
530 } | 529 } |
531 } | 530 } |
532 } | 531 } |
533 | 532 |
534 | 533 |
535 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the | 534 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the |
536 // shift can be a truncating Smi shift-left and result is always Smi. | 535 // shift can be a truncating Smi shift-left and result is always Smi. |
537 // Merging occurs only per basic-block. | 536 // Merging occurs only per basic-block. |
538 void FlowGraphOptimizer::TryOptimizePatterns() { | 537 void AotOptimizer::TryOptimizePatterns() { |
539 if (!FLAG_truncating_left_shift) return; | 538 if (!FLAG_truncating_left_shift) return; |
540 ASSERT(current_iterator_ == NULL); | 539 ASSERT(current_iterator_ == NULL); |
541 GrowableArray<BinarySmiOpInstr*> div_mod_merge; | 540 GrowableArray<BinarySmiOpInstr*> div_mod_merge; |
542 GrowableArray<MathUnaryInstr*> sin_cos_merge; | 541 GrowableArray<MathUnaryInstr*> sin_cos_merge; |
543 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 542 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
544 !block_it.Done(); | 543 !block_it.Done(); |
545 block_it.Advance()) { | 544 block_it.Advance()) { |
546 // Merging only per basic-block. | 545 // Merging only per basic-block. |
547 div_mod_merge.Clear(); | 546 div_mod_merge.Clear(); |
548 sin_cos_merge.Clear(); | 547 sin_cos_merge.Clear(); |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
714 // Unboxed double operation can't handle case of two smis. | 713 // Unboxed double operation can't handle case of two smis. |
715 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | 714 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
716 return false; | 715 return false; |
717 } | 716 } |
718 | 717 |
719 // Check that it have seen only smis and doubles. | 718 // Check that it have seen only smis and doubles. |
720 return HasTwoDoubleOrSmi(ic_data); | 719 return HasTwoDoubleOrSmi(ic_data); |
721 } | 720 } |
722 | 721 |
723 | 722 |
724 void FlowGraphOptimizer::ReplaceCall(Definition* call, | 723 void AotOptimizer::ReplaceCall(Definition* call, |
725 Definition* replacement) { | 724 Definition* replacement) { |
726 // Remove the original push arguments. | 725 // Remove the original push arguments. |
727 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 726 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
728 PushArgumentInstr* push = call->PushArgumentAt(i); | 727 PushArgumentInstr* push = call->PushArgumentAt(i); |
729 push->ReplaceUsesWith(push->value()->definition()); | 728 push->ReplaceUsesWith(push->value()->definition()); |
730 push->RemoveFromGraph(); | 729 push->RemoveFromGraph(); |
731 } | 730 } |
732 call->ReplaceWith(replacement, current_iterator()); | 731 call->ReplaceWith(replacement, current_iterator()); |
733 } | 732 } |
734 | 733 |
735 | 734 |
736 void FlowGraphOptimizer::AddCheckSmi(Definition* to_check, | 735 void AotOptimizer::AddCheckSmi(Definition* to_check, |
737 intptr_t deopt_id, | 736 intptr_t deopt_id, |
738 Environment* deopt_environment, | 737 Environment* deopt_environment, |
739 Instruction* insert_before) { | 738 Instruction* insert_before) { |
740 if (to_check->Type()->ToCid() != kSmiCid) { | 739 if (to_check->Type()->ToCid() != kSmiCid) { |
741 InsertBefore(insert_before, | 740 InsertBefore(insert_before, |
742 new(Z) CheckSmiInstr(new(Z) Value(to_check), | 741 new(Z) CheckSmiInstr(new(Z) Value(to_check), |
743 deopt_id, | 742 deopt_id, |
744 insert_before->token_pos()), | 743 insert_before->token_pos()), |
745 deopt_environment, | 744 deopt_environment, |
746 FlowGraph::kEffect); | 745 FlowGraph::kEffect); |
747 } | 746 } |
748 } | 747 } |
749 | 748 |
750 | 749 |
751 Instruction* FlowGraphOptimizer::GetCheckClass(Definition* to_check, | 750 Instruction* AotOptimizer::GetCheckClass(Definition* to_check, |
752 const ICData& unary_checks, | 751 const ICData& unary_checks, |
753 intptr_t deopt_id, | 752 intptr_t deopt_id, |
754 TokenPosition token_pos) { | 753 TokenPosition token_pos) { |
755 if ((unary_checks.NumberOfUsedChecks() == 1) && | 754 if ((unary_checks.NumberOfUsedChecks() == 1) && |
756 unary_checks.HasReceiverClassId(kSmiCid)) { | 755 unary_checks.HasReceiverClassId(kSmiCid)) { |
757 return new(Z) CheckSmiInstr(new(Z) Value(to_check), | 756 return new(Z) CheckSmiInstr(new(Z) Value(to_check), |
758 deopt_id, | 757 deopt_id, |
759 token_pos); | 758 token_pos); |
760 } | 759 } |
761 return new(Z) CheckClassInstr( | 760 return new(Z) CheckClassInstr( |
762 new(Z) Value(to_check), deopt_id, unary_checks, token_pos); | 761 new(Z) Value(to_check), deopt_id, unary_checks, token_pos); |
763 } | 762 } |
764 | 763 |
765 | 764 |
766 void FlowGraphOptimizer::AddCheckClass(Definition* to_check, | 765 void AotOptimizer::AddCheckClass(Definition* to_check, |
767 const ICData& unary_checks, | 766 const ICData& unary_checks, |
768 intptr_t deopt_id, | 767 intptr_t deopt_id, |
769 Environment* deopt_environment, | 768 Environment* deopt_environment, |
770 Instruction* insert_before) { | 769 Instruction* insert_before) { |
771 // Type propagation has not run yet, we cannot eliminate the check. | 770 // Type propagation has not run yet, we cannot eliminate the check. |
772 Instruction* check = GetCheckClass( | 771 Instruction* check = GetCheckClass( |
773 to_check, unary_checks, deopt_id, insert_before->token_pos()); | 772 to_check, unary_checks, deopt_id, insert_before->token_pos()); |
774 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); | 773 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); |
775 } | 774 } |
776 | 775 |
777 | 776 |
778 void FlowGraphOptimizer::AddReceiverCheck(InstanceCallInstr* call) { | 777 void AotOptimizer::AddReceiverCheck(InstanceCallInstr* call) { |
779 AddCheckClass(call->ArgumentAt(0), | 778 AddCheckClass(call->ArgumentAt(0), |
780 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), | 779 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), |
781 call->deopt_id(), | 780 call->deopt_id(), |
782 call->env(), | 781 call->env(), |
783 call); | 782 call); |
784 } | 783 } |
785 | 784 |
786 | 785 |
787 static bool ArgIsAlways(intptr_t cid, | 786 static bool ArgIsAlways(intptr_t cid, |
788 const ICData& ic_data, | 787 const ICData& ic_data, |
789 intptr_t arg_number) { | 788 intptr_t arg_number) { |
790 ASSERT(ic_data.NumArgsTested() > arg_number); | 789 ASSERT(ic_data.NumArgsTested() > arg_number); |
791 if (ic_data.NumberOfUsedChecks() == 0) { | 790 if (ic_data.NumberOfUsedChecks() == 0) { |
792 return false; | 791 return false; |
793 } | 792 } |
794 const intptr_t num_checks = ic_data.NumberOfChecks(); | 793 const intptr_t num_checks = ic_data.NumberOfChecks(); |
795 for (intptr_t i = 0; i < num_checks; i++) { | 794 for (intptr_t i = 0; i < num_checks; i++) { |
796 if (ic_data.IsUsedAt(i) && ic_data.GetClassIdAt(i, arg_number) != cid) { | 795 if (ic_data.IsUsedAt(i) && ic_data.GetClassIdAt(i, arg_number) != cid) { |
797 return false; | 796 return false; |
798 } | 797 } |
799 } | 798 } |
800 return true; | 799 return true; |
801 } | 800 } |
802 | 801 |
803 | 802 |
804 bool FlowGraphOptimizer::TryReplaceWithIndexedOp(InstanceCallInstr* call) { | 803 bool AotOptimizer::TryReplaceWithIndexedOp(InstanceCallInstr* call) { |
805 // Check for monomorphic IC data. | 804 // Check for monomorphic IC data. |
806 if (!call->HasICData()) return false; | 805 if (!call->HasICData()) return false; |
807 const ICData& ic_data = | 806 const ICData& ic_data = |
808 ICData::Handle(Z, call->ic_data()->AsUnaryClassChecks()); | 807 ICData::Handle(Z, call->ic_data()->AsUnaryClassChecks()); |
809 if (ic_data.NumberOfChecks() != 1) { | 808 if (ic_data.NumberOfChecks() != 1) { |
810 return false; | 809 return false; |
811 } | 810 } |
812 return TryReplaceInstanceCallWithInline(call); | 811 return TryReplaceInstanceCallWithInline(call); |
813 } | 812 } |
814 | 813 |
(...skipping 11 matching lines...) Expand all Loading... | |
826 } else { | 825 } else { |
827 return d->IsStringFromCharCode(); | 826 return d->IsStringFromCharCode(); |
828 } | 827 } |
829 } | 828 } |
830 | 829 |
831 | 830 |
832 // Returns true if the string comparison was converted into char-code | 831 // Returns true if the string comparison was converted into char-code |
833 // comparison. Conversion is only possible for strings of length one. | 832 // comparison. Conversion is only possible for strings of length one. |
834 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes. | 833 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes. |
835 // TODO(srdjan): Expand for two-byte and external strings. | 834 // TODO(srdjan): Expand for two-byte and external strings. |
836 bool FlowGraphOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call, | 835 bool AotOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call, |
837 Token::Kind op_kind) { | 836 Token::Kind op_kind) { |
838 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid)); | 837 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid)); |
839 // Check that left and right are length one strings (either string constants | 838 // Check that left and right are length one strings (either string constants |
840 // or results of string-from-char-code. | 839 // or results of string-from-char-code. |
841 Definition* left = call->ArgumentAt(0); | 840 Definition* left = call->ArgumentAt(0); |
842 Definition* right = call->ArgumentAt(1); | 841 Definition* right = call->ArgumentAt(1); |
843 Value* left_val = NULL; | 842 Value* left_val = NULL; |
844 Definition* to_remove_left = NULL; | 843 Definition* to_remove_left = NULL; |
845 if (IsLengthOneString(right)) { | 844 if (IsLengthOneString(right)) { |
846 // Swap, since we know that both arguments are strings | 845 // Swap, since we know that both arguments are strings |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
913 to_remove_right->RemoveFromGraph(); | 912 to_remove_right->RemoveFromGraph(); |
914 } | 913 } |
915 return true; | 914 return true; |
916 } | 915 } |
917 return false; | 916 return false; |
918 } | 917 } |
919 | 918 |
920 | 919 |
921 static bool SmiFitsInDouble() { return kSmiBits < 53; } | 920 static bool SmiFitsInDouble() { return kSmiBits < 53; } |
922 | 921 |
923 bool FlowGraphOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, | 922 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
924 Token::Kind op_kind) { | 923 Token::Kind op_kind) { |
925 const ICData& ic_data = *call->ic_data(); | 924 const ICData& ic_data = *call->ic_data(); |
926 ASSERT(ic_data.NumArgsTested() == 2); | 925 ASSERT(ic_data.NumArgsTested() == 2); |
927 | 926 |
928 ASSERT(call->ArgumentCount() == 2); | 927 ASSERT(call->ArgumentCount() == 2); |
929 Definition* left = call->ArgumentAt(0); | 928 Definition* left = call->ArgumentAt(0); |
930 Definition* right = call->ArgumentAt(1); | 929 Definition* right = call->ArgumentAt(1); |
931 | 930 |
932 intptr_t cid = kIllegalCid; | 931 intptr_t cid = kIllegalCid; |
933 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { | 932 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1022 op_kind, | 1021 op_kind, |
1023 new(Z) Value(left), | 1022 new(Z) Value(left), |
1024 new(Z) Value(right), | 1023 new(Z) Value(right), |
1025 cid, | 1024 cid, |
1026 call->deopt_id()); | 1025 call->deopt_id()); |
1027 ReplaceCall(call, comp); | 1026 ReplaceCall(call, comp); |
1028 return true; | 1027 return true; |
1029 } | 1028 } |
1030 | 1029 |
1031 | 1030 |
1032 bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, | 1031 bool AotOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, |
1033 Token::Kind op_kind) { | 1032 Token::Kind op_kind) { |
1034 const ICData& ic_data = *call->ic_data(); | 1033 const ICData& ic_data = *call->ic_data(); |
1035 ASSERT(ic_data.NumArgsTested() == 2); | 1034 ASSERT(ic_data.NumArgsTested() == 2); |
1036 | 1035 |
1037 ASSERT(call->ArgumentCount() == 2); | 1036 ASSERT(call->ArgumentCount() == 2); |
1038 Definition* left = call->ArgumentAt(0); | 1037 Definition* left = call->ArgumentAt(0); |
1039 Definition* right = call->ArgumentAt(1); | 1038 Definition* right = call->ArgumentAt(1); |
1040 | 1039 |
1041 intptr_t cid = kIllegalCid; | 1040 intptr_t cid = kIllegalCid; |
1042 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 1041 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1084 op_kind, | 1083 op_kind, |
1085 new(Z) Value(left), | 1084 new(Z) Value(left), |
1086 new(Z) Value(right), | 1085 new(Z) Value(right), |
1087 cid, | 1086 cid, |
1088 call->deopt_id()); | 1087 call->deopt_id()); |
1089 ReplaceCall(call, comp); | 1088 ReplaceCall(call, comp); |
1090 return true; | 1089 return true; |
1091 } | 1090 } |
1092 | 1091 |
1093 | 1092 |
1094 bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, | 1093 bool AotOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, |
1095 Token::Kind op_kind) { | 1094 Token::Kind op_kind) { |
1096 intptr_t operands_type = kIllegalCid; | 1095 intptr_t operands_type = kIllegalCid; |
1097 ASSERT(call->HasICData()); | 1096 ASSERT(call->HasICData()); |
1098 const ICData& ic_data = *call->ic_data(); | 1097 const ICData& ic_data = *call->ic_data(); |
1099 switch (op_kind) { | 1098 switch (op_kind) { |
1100 case Token::kADD: | 1099 case Token::kADD: |
1101 case Token::kSUB: | 1100 case Token::kSUB: |
1102 case Token::kMUL: | 1101 case Token::kMUL: |
1103 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 1102 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
1104 // Don't generate smi code if the IC data is marked because | 1103 // Don't generate smi code if the IC data is marked because |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1293 op_kind, | 1292 op_kind, |
1294 new(Z) Value(left), | 1293 new(Z) Value(left), |
1295 new(Z) Value(right), | 1294 new(Z) Value(right), |
1296 call->deopt_id()); | 1295 call->deopt_id()); |
1297 ReplaceCall(call, bin_op); | 1296 ReplaceCall(call, bin_op); |
1298 } | 1297 } |
1299 return true; | 1298 return true; |
1300 } | 1299 } |
1301 | 1300 |
1302 | 1301 |
1303 bool FlowGraphOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, | 1302 bool AotOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, |
1304 Token::Kind op_kind) { | 1303 Token::Kind op_kind) { |
1305 ASSERT(call->ArgumentCount() == 1); | 1304 ASSERT(call->ArgumentCount() == 1); |
1306 Definition* input = call->ArgumentAt(0); | 1305 Definition* input = call->ArgumentAt(0); |
1307 Definition* unary_op = NULL; | 1306 Definition* unary_op = NULL; |
1308 if (HasOnlyOneSmi(*call->ic_data())) { | 1307 if (HasOnlyOneSmi(*call->ic_data())) { |
1309 InsertBefore(call, | 1308 InsertBefore(call, |
1310 new(Z) CheckSmiInstr(new(Z) Value(input), | 1309 new(Z) CheckSmiInstr(new(Z) Value(input), |
1311 call->deopt_id(), | 1310 call->deopt_id(), |
1312 call->token_pos()), | 1311 call->token_pos()), |
1313 call->env(), | 1312 call->env(), |
(...skipping 14 matching lines...) Expand all Loading... | |
1328 } else { | 1327 } else { |
1329 return false; | 1328 return false; |
1330 } | 1329 } |
1331 ASSERT(unary_op != NULL); | 1330 ASSERT(unary_op != NULL); |
1332 ReplaceCall(call, unary_op); | 1331 ReplaceCall(call, unary_op); |
1333 return true; | 1332 return true; |
1334 } | 1333 } |
1335 | 1334 |
1336 | 1335 |
1337 // Using field class | 1336 // Using field class |
1338 RawField* FlowGraphOptimizer::GetField(intptr_t class_id, | 1337 RawField* AotOptimizer::GetField(intptr_t class_id, |
1339 const String& field_name) { | 1338 const String& field_name) { |
1340 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); | 1339 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); |
1341 Field& field = Field::Handle(Z); | 1340 Field& field = Field::Handle(Z); |
1342 while (!cls.IsNull()) { | 1341 while (!cls.IsNull()) { |
1343 field = cls.LookupInstanceField(field_name); | 1342 field = cls.LookupInstanceField(field_name); |
1344 if (!field.IsNull()) { | 1343 if (!field.IsNull()) { |
1345 return field.raw(); | 1344 return field.raw(); |
1346 } | 1345 } |
1347 cls = cls.SuperClass(); | 1346 cls = cls.SuperClass(); |
1348 } | 1347 } |
1349 return Field::null(); | 1348 return Field::null(); |
1350 } | 1349 } |
1351 | 1350 |
1352 | 1351 |
1353 // Use CHA to determine if the call needs a class check: if the callee's | 1352 // Use CHA to determine if the call needs a class check: if the callee's |
1354 // receiver is the same as the caller's receiver and there are no overriden | 1353 // receiver is the same as the caller's receiver and there are no overriden |
1355 // callee functions, then no class check is needed. | 1354 // callee functions, then no class check is needed. |
1356 bool FlowGraphOptimizer::InstanceCallNeedsClassCheck( | 1355 bool AotOptimizer::InstanceCallNeedsClassCheck( |
1357 InstanceCallInstr* call, RawFunction::Kind kind) const { | 1356 InstanceCallInstr* call, RawFunction::Kind kind) const { |
1358 if (!FLAG_use_cha_deopt && !isolate()->all_classes_finalized()) { | 1357 if (!FLAG_use_cha_deopt && !isolate()->all_classes_finalized()) { |
1359 // Even if class or function are private, lazy class finalization | 1358 // Even if class or function are private, lazy class finalization |
1360 // may later add overriding methods. | 1359 // may later add overriding methods. |
1361 return true; | 1360 return true; |
1362 } | 1361 } |
1363 Definition* callee_receiver = call->ArgumentAt(0); | 1362 Definition* callee_receiver = call->ArgumentAt(0); |
1364 ASSERT(callee_receiver != NULL); | 1363 ASSERT(callee_receiver != NULL); |
1365 const Function& function = flow_graph_->function(); | 1364 const Function& function = flow_graph_->function(); |
1366 if (function.IsDynamicFunction() && | 1365 if (function.IsDynamicFunction() && |
(...skipping 10 matching lines...) Expand all Loading... | |
1377 name.ToCString(), cls.ToCString()); | 1376 name.ToCString(), cls.ToCString()); |
1378 } | 1377 } |
1379 thread()->cha()->AddToLeafClasses(cls); | 1378 thread()->cha()->AddToLeafClasses(cls); |
1380 return false; | 1379 return false; |
1381 } | 1380 } |
1382 } | 1381 } |
1383 return true; | 1382 return true; |
1384 } | 1383 } |
1385 | 1384 |
1386 | 1385 |
1387 bool FlowGraphOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call, | 1386 bool AotOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call, |
1388 bool allow_check) { | 1387 bool allow_check) { |
1389 ASSERT(call->HasICData()); | 1388 ASSERT(call->HasICData()); |
1390 const ICData& ic_data = *call->ic_data(); | 1389 const ICData& ic_data = *call->ic_data(); |
1391 ASSERT(ic_data.HasOneTarget()); | 1390 ASSERT(ic_data.HasOneTarget()); |
1392 GrowableArray<intptr_t> class_ids; | 1391 GrowableArray<intptr_t> class_ids; |
1393 ic_data.GetClassIdsAt(0, &class_ids); | 1392 ic_data.GetClassIdsAt(0, &class_ids); |
1394 ASSERT(class_ids.length() == 1); | 1393 ASSERT(class_ids.length() == 1); |
1395 // Inline implicit instance getter. | 1394 // Inline implicit instance getter. |
1396 const String& field_name = | 1395 const String& field_name = |
1397 String::Handle(Z, Field::NameFromGetter(call->function_name())); | 1396 String::Handle(Z, Field::NameFromGetter(call->function_name())); |
(...skipping 30 matching lines...) Expand all Loading... | |
1428 for (Value::Iterator it(load->input_use_list()); | 1427 for (Value::Iterator it(load->input_use_list()); |
1429 !it.Done(); | 1428 !it.Done(); |
1430 it.Advance()) { | 1429 it.Advance()) { |
1431 it.Current()->SetReachingType(NULL); | 1430 it.Current()->SetReachingType(NULL); |
1432 } | 1431 } |
1433 } | 1432 } |
1434 return true; | 1433 return true; |
1435 } | 1434 } |
1436 | 1435 |
1437 | 1436 |
1438 bool FlowGraphOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call, | 1437 bool AotOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call, |
1439 MethodRecognizer::Kind getter) { | 1438 MethodRecognizer::Kind getter) { |
1440 if (!ShouldInlineSimd()) { | 1439 if (!ShouldInlineSimd()) { |
1441 return false; | 1440 return false; |
1442 } | 1441 } |
1443 AddCheckClass(call->ArgumentAt(0), | 1442 AddCheckClass(call->ArgumentAt(0), |
1444 ICData::ZoneHandle( | 1443 ICData::ZoneHandle( |
1445 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1444 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
1446 call->deopt_id(), | 1445 call->deopt_id(), |
1447 call->env(), | 1446 call->env(), |
1448 call); | 1447 call); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1503 mask, | 1502 mask, |
1504 call->deopt_id()); | 1503 call->deopt_id()); |
1505 ReplaceCall(call, instr); | 1504 ReplaceCall(call, instr); |
1506 return true; | 1505 return true; |
1507 } | 1506 } |
1508 UNREACHABLE(); | 1507 UNREACHABLE(); |
1509 return false; | 1508 return false; |
1510 } | 1509 } |
1511 | 1510 |
1512 | 1511 |
1513 bool FlowGraphOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call, | 1512 bool AotOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call, |
1514 MethodRecognizer::Kind getter) { | 1513 MethodRecognizer::Kind getter) { |
1515 if (!ShouldInlineSimd()) { | 1514 if (!ShouldInlineSimd()) { |
1516 return false; | 1515 return false; |
1517 } | 1516 } |
1518 AddCheckClass(call->ArgumentAt(0), | 1517 AddCheckClass(call->ArgumentAt(0), |
1519 ICData::ZoneHandle( | 1518 ICData::ZoneHandle( |
1520 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1519 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
1521 call->deopt_id(), | 1520 call->deopt_id(), |
1522 call->env(), | 1521 call->env(), |
1523 call); | 1522 call); |
1524 if ((getter == MethodRecognizer::kFloat64x2GetX) || | 1523 if ((getter == MethodRecognizer::kFloat64x2GetX) || |
1525 (getter == MethodRecognizer::kFloat64x2GetY)) { | 1524 (getter == MethodRecognizer::kFloat64x2GetY)) { |
1526 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr( | 1525 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr( |
1527 getter, | 1526 getter, |
1528 new(Z) Value(call->ArgumentAt(0)), | 1527 new(Z) Value(call->ArgumentAt(0)), |
1529 0, | 1528 0, |
1530 call->deopt_id()); | 1529 call->deopt_id()); |
1531 ReplaceCall(call, instr); | 1530 ReplaceCall(call, instr); |
1532 return true; | 1531 return true; |
1533 } | 1532 } |
1534 UNREACHABLE(); | 1533 UNREACHABLE(); |
1535 return false; | 1534 return false; |
1536 } | 1535 } |
1537 | 1536 |
1538 | 1537 |
1539 bool FlowGraphOptimizer::InlineInt32x4Getter(InstanceCallInstr* call, | 1538 bool AotOptimizer::InlineInt32x4Getter(InstanceCallInstr* call, |
1540 MethodRecognizer::Kind getter) { | 1539 MethodRecognizer::Kind getter) { |
1541 if (!ShouldInlineSimd()) { | 1540 if (!ShouldInlineSimd()) { |
1542 return false; | 1541 return false; |
1543 } | 1542 } |
1544 AddCheckClass(call->ArgumentAt(0), | 1543 AddCheckClass(call->ArgumentAt(0), |
1545 ICData::ZoneHandle( | 1544 ICData::ZoneHandle( |
1546 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1545 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
1547 call->deopt_id(), | 1546 call->deopt_id(), |
1548 call->env(), | 1547 call->env(), |
1549 call); | 1548 call); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1604 Int32x4GetFlagInstr* instr = new(Z) Int32x4GetFlagInstr( | 1603 Int32x4GetFlagInstr* instr = new(Z) Int32x4GetFlagInstr( |
1605 getter, | 1604 getter, |
1606 new(Z) Value(call->ArgumentAt(0)), | 1605 new(Z) Value(call->ArgumentAt(0)), |
1607 call->deopt_id()); | 1606 call->deopt_id()); |
1608 ReplaceCall(call, instr); | 1607 ReplaceCall(call, instr); |
1609 return true; | 1608 return true; |
1610 } | 1609 } |
1611 } | 1610 } |
1612 | 1611 |
1613 | 1612 |
1614 bool FlowGraphOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, | 1613 bool AotOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, |
1615 Token::Kind op_kind) { | 1614 Token::Kind op_kind) { |
1616 if (!ShouldInlineSimd()) { | 1615 if (!ShouldInlineSimd()) { |
1617 return false; | 1616 return false; |
1618 } | 1617 } |
1619 ASSERT(call->ArgumentCount() == 2); | 1618 ASSERT(call->ArgumentCount() == 2); |
1620 Definition* left = call->ArgumentAt(0); | 1619 Definition* left = call->ArgumentAt(0); |
1621 Definition* right = call->ArgumentAt(1); | 1620 Definition* right = call->ArgumentAt(1); |
1622 // Type check left. | 1621 // Type check left. |
1623 AddCheckClass(left, | 1622 AddCheckClass(left, |
1624 ICData::ZoneHandle( | 1623 ICData::ZoneHandle( |
(...skipping 12 matching lines...) Expand all Loading... | |
1637 BinaryFloat32x4OpInstr* float32x4_bin_op = | 1636 BinaryFloat32x4OpInstr* float32x4_bin_op = |
1638 new(Z) BinaryFloat32x4OpInstr( | 1637 new(Z) BinaryFloat32x4OpInstr( |
1639 op_kind, new(Z) Value(left), new(Z) Value(right), | 1638 op_kind, new(Z) Value(left), new(Z) Value(right), |
1640 call->deopt_id()); | 1639 call->deopt_id()); |
1641 ReplaceCall(call, float32x4_bin_op); | 1640 ReplaceCall(call, float32x4_bin_op); |
1642 | 1641 |
1643 return true; | 1642 return true; |
1644 } | 1643 } |
1645 | 1644 |
1646 | 1645 |
1647 bool FlowGraphOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, | 1646 bool AotOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, |
1648 Token::Kind op_kind) { | 1647 Token::Kind op_kind) { |
1649 if (!ShouldInlineSimd()) { | 1648 if (!ShouldInlineSimd()) { |
1650 return false; | 1649 return false; |
1651 } | 1650 } |
1652 ASSERT(call->ArgumentCount() == 2); | 1651 ASSERT(call->ArgumentCount() == 2); |
1653 Definition* left = call->ArgumentAt(0); | 1652 Definition* left = call->ArgumentAt(0); |
1654 Definition* right = call->ArgumentAt(1); | 1653 Definition* right = call->ArgumentAt(1); |
1655 // Type check left. | 1654 // Type check left. |
1656 AddCheckClass(left, | 1655 AddCheckClass(left, |
1657 ICData::ZoneHandle( | 1656 ICData::ZoneHandle( |
(...skipping 11 matching lines...) Expand all Loading... | |
1669 // Replace call. | 1668 // Replace call. |
1670 BinaryInt32x4OpInstr* int32x4_bin_op = | 1669 BinaryInt32x4OpInstr* int32x4_bin_op = |
1671 new(Z) BinaryInt32x4OpInstr( | 1670 new(Z) BinaryInt32x4OpInstr( |
1672 op_kind, new(Z) Value(left), new(Z) Value(right), | 1671 op_kind, new(Z) Value(left), new(Z) Value(right), |
1673 call->deopt_id()); | 1672 call->deopt_id()); |
1674 ReplaceCall(call, int32x4_bin_op); | 1673 ReplaceCall(call, int32x4_bin_op); |
1675 return true; | 1674 return true; |
1676 } | 1675 } |
1677 | 1676 |
1678 | 1677 |
1679 bool FlowGraphOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, | 1678 bool AotOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, |
1680 Token::Kind op_kind) { | 1679 Token::Kind op_kind) { |
1681 if (!ShouldInlineSimd()) { | 1680 if (!ShouldInlineSimd()) { |
1682 return false; | 1681 return false; |
1683 } | 1682 } |
1684 ASSERT(call->ArgumentCount() == 2); | 1683 ASSERT(call->ArgumentCount() == 2); |
1685 Definition* left = call->ArgumentAt(0); | 1684 Definition* left = call->ArgumentAt(0); |
1686 Definition* right = call->ArgumentAt(1); | 1685 Definition* right = call->ArgumentAt(1); |
1687 // Type check left. | 1686 // Type check left. |
1688 AddCheckClass(left, | 1687 AddCheckClass(left, |
1689 ICData::ZoneHandle( | 1688 ICData::ZoneHandle( |
(...skipping 13 matching lines...) Expand all Loading... | |
1703 new(Z) BinaryFloat64x2OpInstr( | 1702 new(Z) BinaryFloat64x2OpInstr( |
1704 op_kind, new(Z) Value(left), new(Z) Value(right), | 1703 op_kind, new(Z) Value(left), new(Z) Value(right), |
1705 call->deopt_id()); | 1704 call->deopt_id()); |
1706 ReplaceCall(call, float64x2_bin_op); | 1705 ReplaceCall(call, float64x2_bin_op); |
1707 return true; | 1706 return true; |
1708 } | 1707 } |
1709 | 1708 |
1710 | 1709 |
1711 // Only unique implicit instance getters can be currently handled. | 1710 // Only unique implicit instance getters can be currently handled. |
1712 // Returns false if 'allow_check' is false and a check is needed. | 1711 // Returns false if 'allow_check' is false and a check is needed. |
1713 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call, | 1712 bool AotOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call, |
1714 bool allow_check) { | 1713 bool allow_check) { |
1715 ASSERT(call->HasICData()); | 1714 ASSERT(call->HasICData()); |
1716 const ICData& ic_data = *call->ic_data(); | 1715 const ICData& ic_data = *call->ic_data(); |
1717 if (ic_data.NumberOfUsedChecks() == 0) { | 1716 if (ic_data.NumberOfUsedChecks() == 0) { |
1718 // No type feedback collected. | 1717 // No type feedback collected. |
1719 return false; | 1718 return false; |
1720 } | 1719 } |
1721 | 1720 |
1722 if (!ic_data.HasOneTarget()) { | 1721 if (!ic_data.HasOneTarget()) { |
1723 // Polymorphic sites are inlined like normal methods by conventional | 1722 // Polymorphic sites are inlined like normal methods by conventional |
1724 // inlining in FlowGraphInliner. | 1723 // inlining in FlowGraphInliner. |
1725 return false; | 1724 return false; |
1726 } | 1725 } |
1727 | 1726 |
1728 const Function& target = Function::Handle(Z, ic_data.GetTargetAt(0)); | 1727 const Function& target = Function::Handle(Z, ic_data.GetTargetAt(0)); |
1729 if (target.kind() != RawFunction::kImplicitGetter) { | 1728 if (target.kind() != RawFunction::kImplicitGetter) { |
1730 // Non-implicit getters are inlined like normal methods by conventional | 1729 // Non-implicit getters are inlined like normal methods by conventional |
1731 // inlining in FlowGraphInliner. | 1730 // inlining in FlowGraphInliner. |
1732 return false; | 1731 return false; |
1733 } | 1732 } |
1734 return InlineImplicitInstanceGetter(call, allow_check); | 1733 return InlineImplicitInstanceGetter(call, allow_check); |
1735 } | 1734 } |
1736 | 1735 |
1737 | 1736 |
1738 bool FlowGraphOptimizer::TryReplaceInstanceCallWithInline( | 1737 bool AotOptimizer::TryReplaceInstanceCallWithInline( |
1739 InstanceCallInstr* call) { | 1738 InstanceCallInstr* call) { |
1740 Function& target = Function::Handle(Z); | 1739 Function& target = Function::Handle(Z); |
1741 GrowableArray<intptr_t> class_ids; | 1740 GrowableArray<intptr_t> class_ids; |
1742 call->ic_data()->GetCheckAt(0, &class_ids, &target); | 1741 call->ic_data()->GetCheckAt(0, &class_ids, &target); |
1743 const intptr_t receiver_cid = class_ids[0]; | 1742 const intptr_t receiver_cid = class_ids[0]; |
1744 | 1743 |
1745 TargetEntryInstr* entry; | 1744 TargetEntryInstr* entry; |
1746 Definition* last; | 1745 Definition* last; |
1747 if (!FlowGraphInliner::TryInlineRecognizedMethod(flow_graph_, | 1746 if (!FlowGraphInliner::TryInlineRecognizedMethod(flow_graph_, |
1748 receiver_cid, | 1747 receiver_cid, |
(...skipping 23 matching lines...) Expand all Loading... | |
1772 last->LinkTo(call); | 1771 last->LinkTo(call); |
1773 // Remove through the iterator. | 1772 // Remove through the iterator. |
1774 ASSERT(current_iterator()->Current() == call); | 1773 ASSERT(current_iterator()->Current() == call); |
1775 current_iterator()->RemoveCurrentFromGraph(); | 1774 current_iterator()->RemoveCurrentFromGraph(); |
1776 call->set_previous(NULL); | 1775 call->set_previous(NULL); |
1777 call->set_next(NULL); | 1776 call->set_next(NULL); |
1778 return true; | 1777 return true; |
1779 } | 1778 } |
1780 | 1779 |
1781 | 1780 |
1782 void FlowGraphOptimizer::ReplaceWithMathCFunction( | 1781 void AotOptimizer::ReplaceWithMathCFunction( |
1783 InstanceCallInstr* call, | 1782 InstanceCallInstr* call, |
1784 MethodRecognizer::Kind recognized_kind) { | 1783 MethodRecognizer::Kind recognized_kind) { |
1785 AddReceiverCheck(call); | 1784 AddReceiverCheck(call); |
1786 ZoneGrowableArray<Value*>* args = | 1785 ZoneGrowableArray<Value*>* args = |
1787 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount()); | 1786 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount()); |
1788 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { | 1787 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { |
1789 args->Add(new(Z) Value(call->ArgumentAt(i))); | 1788 args->Add(new(Z) Value(call->ArgumentAt(i))); |
1790 } | 1789 } |
1791 InvokeMathCFunctionInstr* invoke = | 1790 InvokeMathCFunctionInstr* invoke = |
1792 new(Z) InvokeMathCFunctionInstr(args, | 1791 new(Z) InvokeMathCFunctionInstr(args, |
(...skipping 20 matching lines...) Expand all Loading... | |
1813 case kTypedDataFloat32x4ArrayCid: | 1812 case kTypedDataFloat32x4ArrayCid: |
1814 case kTypedDataInt32x4ArrayCid: | 1813 case kTypedDataInt32x4ArrayCid: |
1815 return true; | 1814 return true; |
1816 default: | 1815 default: |
1817 return false; | 1816 return false; |
1818 } | 1817 } |
1819 } | 1818 } |
1820 | 1819 |
1821 | 1820 |
1822 // Inline only simple, frequently called core library methods. | 1821 // Inline only simple, frequently called core library methods. |
1823 bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { | 1822 bool AotOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { |
1824 ASSERT(call->HasICData()); | 1823 ASSERT(call->HasICData()); |
1825 const ICData& ic_data = *call->ic_data(); | 1824 const ICData& ic_data = *call->ic_data(); |
1826 if ((ic_data.NumberOfUsedChecks() == 0) || !ic_data.HasOneTarget()) { | 1825 if ((ic_data.NumberOfUsedChecks() == 0) || !ic_data.HasOneTarget()) { |
1827 // No type feedback collected or multiple targets found. | 1826 // No type feedback collected or multiple targets found. |
1828 return false; | 1827 return false; |
1829 } | 1828 } |
1830 | 1829 |
1831 Function& target = Function::Handle(Z); | 1830 Function& target = Function::Handle(Z); |
1832 GrowableArray<intptr_t> class_ids; | 1831 GrowableArray<intptr_t> class_ids; |
1833 ic_data.GetCheckAt(0, &class_ids, &target); | 1832 ic_data.GetCheckAt(0, &class_ids, &target); |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2046 new(Z) Value(int32_mask), | 2045 new(Z) Value(int32_mask), |
2047 call->deopt_id()); | 2046 call->deopt_id()); |
2048 ReplaceCall(call, bit_and); | 2047 ReplaceCall(call, bit_and); |
2049 return true; | 2048 return true; |
2050 } | 2049 } |
2051 } | 2050 } |
2052 return false; | 2051 return false; |
2053 } | 2052 } |
2054 | 2053 |
2055 | 2054 |
2056 bool FlowGraphOptimizer::TryInlineFloat32x4Constructor( | 2055 bool AotOptimizer::TryInlineFloat32x4Constructor( |
2057 StaticCallInstr* call, | 2056 StaticCallInstr* call, |
2058 MethodRecognizer::Kind recognized_kind) { | 2057 MethodRecognizer::Kind recognized_kind) { |
2059 if (FLAG_precompilation) { | 2058 ASSERT(FLAG_precompilation); |
2060 // Cannot handle unboxed instructions. | |
2061 return false; | |
2062 } | |
2063 if (!ShouldInlineSimd()) { | |
2064 return false; | |
2065 } | |
2066 if (recognized_kind == MethodRecognizer::kFloat32x4Zero) { | |
2067 Float32x4ZeroInstr* zero = new(Z) Float32x4ZeroInstr(); | |
2068 ReplaceCall(call, zero); | |
2069 return true; | |
2070 } else if (recognized_kind == MethodRecognizer::kFloat32x4Splat) { | |
2071 Float32x4SplatInstr* splat = | |
2072 new(Z) Float32x4SplatInstr( | |
2073 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2074 ReplaceCall(call, splat); | |
2075 return true; | |
2076 } else if (recognized_kind == MethodRecognizer::kFloat32x4Constructor) { | |
2077 Float32x4ConstructorInstr* con = | |
2078 new(Z) Float32x4ConstructorInstr( | |
2079 new(Z) Value(call->ArgumentAt(1)), | |
2080 new(Z) Value(call->ArgumentAt(2)), | |
2081 new(Z) Value(call->ArgumentAt(3)), | |
2082 new(Z) Value(call->ArgumentAt(4)), | |
2083 call->deopt_id()); | |
2084 ReplaceCall(call, con); | |
2085 return true; | |
2086 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromInt32x4Bits) { | |
2087 Int32x4ToFloat32x4Instr* cast = | |
2088 new(Z) Int32x4ToFloat32x4Instr( | |
2089 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2090 ReplaceCall(call, cast); | |
2091 return true; | |
2092 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromFloat64x2) { | |
2093 Float64x2ToFloat32x4Instr* cast = | |
2094 new(Z) Float64x2ToFloat32x4Instr( | |
2095 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2096 ReplaceCall(call, cast); | |
2097 return true; | |
2098 } | |
2099 return false; | 2059 return false; |
2100 } | 2060 } |
2101 | 2061 |
2102 | 2062 |
2103 bool FlowGraphOptimizer::TryInlineFloat64x2Constructor( | 2063 bool AotOptimizer::TryInlineFloat64x2Constructor( |
2104 StaticCallInstr* call, | 2064 StaticCallInstr* call, |
2105 MethodRecognizer::Kind recognized_kind) { | 2065 MethodRecognizer::Kind recognized_kind) { |
2106 if (FLAG_precompilation) { | 2066 ASSERT(FLAG_precompilation); |
2107 // Cannot handle unboxed instructions. | |
2108 return false; | |
2109 } | |
2110 if (!ShouldInlineSimd()) { | |
2111 return false; | |
2112 } | |
2113 if (recognized_kind == MethodRecognizer::kFloat64x2Zero) { | |
2114 Float64x2ZeroInstr* zero = new(Z) Float64x2ZeroInstr(); | |
2115 ReplaceCall(call, zero); | |
2116 return true; | |
2117 } else if (recognized_kind == MethodRecognizer::kFloat64x2Splat) { | |
2118 Float64x2SplatInstr* splat = | |
2119 new(Z) Float64x2SplatInstr( | |
2120 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2121 ReplaceCall(call, splat); | |
2122 return true; | |
2123 } else if (recognized_kind == MethodRecognizer::kFloat64x2Constructor) { | |
2124 Float64x2ConstructorInstr* con = | |
2125 new(Z) Float64x2ConstructorInstr( | |
2126 new(Z) Value(call->ArgumentAt(1)), | |
2127 new(Z) Value(call->ArgumentAt(2)), | |
2128 call->deopt_id()); | |
2129 ReplaceCall(call, con); | |
2130 return true; | |
2131 } else if (recognized_kind == MethodRecognizer::kFloat64x2FromFloat32x4) { | |
2132 Float32x4ToFloat64x2Instr* cast = | |
2133 new(Z) Float32x4ToFloat64x2Instr( | |
2134 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2135 ReplaceCall(call, cast); | |
2136 return true; | |
2137 } | |
2138 return false; | 2067 return false; |
2139 } | 2068 } |
2140 | 2069 |
2141 | 2070 |
2142 bool FlowGraphOptimizer::TryInlineInt32x4Constructor( | 2071 bool AotOptimizer::TryInlineInt32x4Constructor( |
2143 StaticCallInstr* call, | 2072 StaticCallInstr* call, |
2144 MethodRecognizer::Kind recognized_kind) { | 2073 MethodRecognizer::Kind recognized_kind) { |
2145 if (FLAG_precompilation) { | 2074 ASSERT(FLAG_precompilation); |
2146 // Cannot handle unboxed instructions. | |
2147 return false; | |
2148 } | |
2149 if (!ShouldInlineSimd()) { | |
2150 return false; | |
2151 } | |
2152 if (recognized_kind == MethodRecognizer::kInt32x4BoolConstructor) { | |
2153 Int32x4BoolConstructorInstr* con = | |
2154 new(Z) Int32x4BoolConstructorInstr( | |
2155 new(Z) Value(call->ArgumentAt(1)), | |
2156 new(Z) Value(call->ArgumentAt(2)), | |
2157 new(Z) Value(call->ArgumentAt(3)), | |
2158 new(Z) Value(call->ArgumentAt(4)), | |
2159 call->deopt_id()); | |
2160 ReplaceCall(call, con); | |
2161 return true; | |
2162 } else if (recognized_kind == MethodRecognizer::kInt32x4FromFloat32x4Bits) { | |
2163 Float32x4ToInt32x4Instr* cast = | |
2164 new(Z) Float32x4ToInt32x4Instr( | |
2165 new(Z) Value(call->ArgumentAt(1)), call->deopt_id()); | |
2166 ReplaceCall(call, cast); | |
2167 return true; | |
2168 } else if (recognized_kind == MethodRecognizer::kInt32x4Constructor) { | |
2169 Int32x4ConstructorInstr* con = | |
2170 new(Z) Int32x4ConstructorInstr( | |
2171 new(Z) Value(call->ArgumentAt(1)), | |
2172 new(Z) Value(call->ArgumentAt(2)), | |
2173 new(Z) Value(call->ArgumentAt(3)), | |
2174 new(Z) Value(call->ArgumentAt(4)), | |
2175 call->deopt_id()); | |
2176 ReplaceCall(call, con); | |
2177 return true; | |
2178 } | |
2179 return false; | 2075 return false; |
2180 } | 2076 } |
2181 | 2077 |
2182 | 2078 |
2183 bool FlowGraphOptimizer::TryInlineFloat32x4Method( | 2079 bool AotOptimizer::TryInlineFloat32x4Method( |
2184 InstanceCallInstr* call, | 2080 InstanceCallInstr* call, |
2185 MethodRecognizer::Kind recognized_kind) { | 2081 MethodRecognizer::Kind recognized_kind) { |
2186 if (!ShouldInlineSimd()) { | 2082 return false; |
2187 return false; | |
2188 } | |
2189 ASSERT(call->HasICData()); | |
2190 switch (recognized_kind) { | |
2191 case MethodRecognizer::kFloat32x4ShuffleX: | |
2192 case MethodRecognizer::kFloat32x4ShuffleY: | |
2193 case MethodRecognizer::kFloat32x4ShuffleZ: | |
2194 case MethodRecognizer::kFloat32x4ShuffleW: | |
2195 case MethodRecognizer::kFloat32x4GetSignMask: | |
2196 ASSERT(call->ic_data()->HasReceiverClassId(kFloat32x4Cid)); | |
2197 ASSERT(call->ic_data()->HasOneTarget()); | |
2198 return InlineFloat32x4Getter(call, recognized_kind); | |
2199 | |
2200 case MethodRecognizer::kFloat32x4Equal: | |
2201 case MethodRecognizer::kFloat32x4GreaterThan: | |
2202 case MethodRecognizer::kFloat32x4GreaterThanOrEqual: | |
2203 case MethodRecognizer::kFloat32x4LessThan: | |
2204 case MethodRecognizer::kFloat32x4LessThanOrEqual: | |
2205 case MethodRecognizer::kFloat32x4NotEqual: { | |
2206 Definition* left = call->ArgumentAt(0); | |
2207 Definition* right = call->ArgumentAt(1); | |
2208 // Type check left. | |
2209 AddCheckClass(left, | |
2210 ICData::ZoneHandle( | |
2211 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2212 call->deopt_id(), | |
2213 call->env(), | |
2214 call); | |
2215 // Replace call. | |
2216 Float32x4ComparisonInstr* cmp = | |
2217 new(Z) Float32x4ComparisonInstr(recognized_kind, | |
2218 new(Z) Value(left), | |
2219 new(Z) Value(right), | |
2220 call->deopt_id()); | |
2221 ReplaceCall(call, cmp); | |
2222 return true; | |
2223 } | |
2224 case MethodRecognizer::kFloat32x4Min: | |
2225 case MethodRecognizer::kFloat32x4Max: { | |
2226 Definition* left = call->ArgumentAt(0); | |
2227 Definition* right = call->ArgumentAt(1); | |
2228 // Type check left. | |
2229 AddCheckClass(left, | |
2230 ICData::ZoneHandle( | |
2231 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2232 call->deopt_id(), | |
2233 call->env(), | |
2234 call); | |
2235 Float32x4MinMaxInstr* minmax = | |
2236 new(Z) Float32x4MinMaxInstr( | |
2237 recognized_kind, | |
2238 new(Z) Value(left), | |
2239 new(Z) Value(right), | |
2240 call->deopt_id()); | |
2241 ReplaceCall(call, minmax); | |
2242 return true; | |
2243 } | |
2244 case MethodRecognizer::kFloat32x4Scale: { | |
2245 Definition* left = call->ArgumentAt(0); | |
2246 Definition* right = call->ArgumentAt(1); | |
2247 // Type check left. | |
2248 AddCheckClass(left, | |
2249 ICData::ZoneHandle( | |
2250 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2251 call->deopt_id(), | |
2252 call->env(), | |
2253 call); | |
2254 // Left and right values are swapped when handed to the instruction, | |
2255 // this is done so that the double value is loaded into the output | |
2256 // register and can be destroyed. | |
2257 Float32x4ScaleInstr* scale = | |
2258 new(Z) Float32x4ScaleInstr(recognized_kind, | |
2259 new(Z) Value(right), | |
2260 new(Z) Value(left), | |
2261 call->deopt_id()); | |
2262 ReplaceCall(call, scale); | |
2263 return true; | |
2264 } | |
2265 case MethodRecognizer::kFloat32x4Sqrt: | |
2266 case MethodRecognizer::kFloat32x4ReciprocalSqrt: | |
2267 case MethodRecognizer::kFloat32x4Reciprocal: { | |
2268 Definition* left = call->ArgumentAt(0); | |
2269 AddCheckClass(left, | |
2270 ICData::ZoneHandle( | |
2271 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2272 call->deopt_id(), | |
2273 call->env(), | |
2274 call); | |
2275 Float32x4SqrtInstr* sqrt = | |
2276 new(Z) Float32x4SqrtInstr(recognized_kind, | |
2277 new(Z) Value(left), | |
2278 call->deopt_id()); | |
2279 ReplaceCall(call, sqrt); | |
2280 return true; | |
2281 } | |
2282 case MethodRecognizer::kFloat32x4WithX: | |
2283 case MethodRecognizer::kFloat32x4WithY: | |
2284 case MethodRecognizer::kFloat32x4WithZ: | |
2285 case MethodRecognizer::kFloat32x4WithW: { | |
2286 Definition* left = call->ArgumentAt(0); | |
2287 Definition* right = call->ArgumentAt(1); | |
2288 // Type check left. | |
2289 AddCheckClass(left, | |
2290 ICData::ZoneHandle( | |
2291 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2292 call->deopt_id(), | |
2293 call->env(), | |
2294 call); | |
2295 Float32x4WithInstr* with = new(Z) Float32x4WithInstr(recognized_kind, | |
2296 new(Z) Value(left), | |
2297 new(Z) Value(right), | |
2298 call->deopt_id()); | |
2299 ReplaceCall(call, with); | |
2300 return true; | |
2301 } | |
2302 case MethodRecognizer::kFloat32x4Absolute: | |
2303 case MethodRecognizer::kFloat32x4Negate: { | |
2304 Definition* left = call->ArgumentAt(0); | |
2305 // Type check left. | |
2306 AddCheckClass(left, | |
2307 ICData::ZoneHandle( | |
2308 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2309 call->deopt_id(), | |
2310 call->env(), | |
2311 call); | |
2312 Float32x4ZeroArgInstr* zeroArg = | |
2313 new(Z) Float32x4ZeroArgInstr( | |
2314 recognized_kind, new(Z) Value(left), call->deopt_id()); | |
2315 ReplaceCall(call, zeroArg); | |
2316 return true; | |
2317 } | |
2318 case MethodRecognizer::kFloat32x4Clamp: { | |
2319 Definition* left = call->ArgumentAt(0); | |
2320 Definition* lower = call->ArgumentAt(1); | |
2321 Definition* upper = call->ArgumentAt(2); | |
2322 // Type check left. | |
2323 AddCheckClass(left, | |
2324 ICData::ZoneHandle( | |
2325 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2326 call->deopt_id(), | |
2327 call->env(), | |
2328 call); | |
2329 Float32x4ClampInstr* clamp = new(Z) Float32x4ClampInstr( | |
2330 new(Z) Value(left), | |
2331 new(Z) Value(lower), | |
2332 new(Z) Value(upper), | |
2333 call->deopt_id()); | |
2334 ReplaceCall(call, clamp); | |
2335 return true; | |
2336 } | |
2337 case MethodRecognizer::kFloat32x4ShuffleMix: | |
2338 case MethodRecognizer::kFloat32x4Shuffle: { | |
2339 return InlineFloat32x4Getter(call, recognized_kind); | |
2340 } | |
2341 default: | |
2342 return false; | |
2343 } | |
2344 } | 2083 } |
2345 | 2084 |
2346 | 2085 |
2347 bool FlowGraphOptimizer::TryInlineFloat64x2Method( | 2086 bool AotOptimizer::TryInlineFloat64x2Method( |
2348 InstanceCallInstr* call, | 2087 InstanceCallInstr* call, |
2349 MethodRecognizer::Kind recognized_kind) { | 2088 MethodRecognizer::Kind recognized_kind) { |
2350 if (!ShouldInlineSimd()) { | 2089 return false; |
2351 return false; | |
2352 } | |
2353 ASSERT(call->HasICData()); | |
2354 switch (recognized_kind) { | |
2355 case MethodRecognizer::kFloat64x2GetX: | |
2356 case MethodRecognizer::kFloat64x2GetY: | |
2357 ASSERT(call->ic_data()->HasReceiverClassId(kFloat64x2Cid)); | |
2358 ASSERT(call->ic_data()->HasOneTarget()); | |
2359 return InlineFloat64x2Getter(call, recognized_kind); | |
2360 case MethodRecognizer::kFloat64x2Negate: | |
2361 case MethodRecognizer::kFloat64x2Abs: | |
2362 case MethodRecognizer::kFloat64x2Sqrt: | |
2363 case MethodRecognizer::kFloat64x2GetSignMask: { | |
2364 Definition* left = call->ArgumentAt(0); | |
2365 // Type check left. | |
2366 AddCheckClass(left, | |
2367 ICData::ZoneHandle( | |
2368 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2369 call->deopt_id(), | |
2370 call->env(), | |
2371 call); | |
2372 Float64x2ZeroArgInstr* zeroArg = | |
2373 new(Z) Float64x2ZeroArgInstr( | |
2374 recognized_kind, new(Z) Value(left), call->deopt_id()); | |
2375 ReplaceCall(call, zeroArg); | |
2376 return true; | |
2377 } | |
2378 case MethodRecognizer::kFloat64x2Scale: | |
2379 case MethodRecognizer::kFloat64x2WithX: | |
2380 case MethodRecognizer::kFloat64x2WithY: | |
2381 case MethodRecognizer::kFloat64x2Min: | |
2382 case MethodRecognizer::kFloat64x2Max: { | |
2383 Definition* left = call->ArgumentAt(0); | |
2384 Definition* right = call->ArgumentAt(1); | |
2385 // Type check left. | |
2386 AddCheckClass(left, | |
2387 ICData::ZoneHandle( | |
2388 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2389 call->deopt_id(), | |
2390 call->env(), | |
2391 call); | |
2392 Float64x2OneArgInstr* zeroArg = | |
2393 new(Z) Float64x2OneArgInstr(recognized_kind, | |
2394 new(Z) Value(left), | |
2395 new(Z) Value(right), | |
2396 call->deopt_id()); | |
2397 ReplaceCall(call, zeroArg); | |
2398 return true; | |
2399 } | |
2400 default: | |
2401 return false; | |
2402 } | |
2403 } | 2090 } |
2404 | 2091 |
2405 | 2092 |
2406 bool FlowGraphOptimizer::TryInlineInt32x4Method( | 2093 bool AotOptimizer::TryInlineInt32x4Method( |
2407 InstanceCallInstr* call, | 2094 InstanceCallInstr* call, |
2408 MethodRecognizer::Kind recognized_kind) { | 2095 MethodRecognizer::Kind recognized_kind) { |
2409 if (!ShouldInlineSimd()) { | 2096 return false; |
2410 return false; | |
2411 } | |
2412 ASSERT(call->HasICData()); | |
2413 switch (recognized_kind) { | |
2414 case MethodRecognizer::kInt32x4ShuffleMix: | |
2415 case MethodRecognizer::kInt32x4Shuffle: | |
2416 case MethodRecognizer::kInt32x4GetFlagX: | |
2417 case MethodRecognizer::kInt32x4GetFlagY: | |
2418 case MethodRecognizer::kInt32x4GetFlagZ: | |
2419 case MethodRecognizer::kInt32x4GetFlagW: | |
2420 case MethodRecognizer::kInt32x4GetSignMask: | |
2421 ASSERT(call->ic_data()->HasReceiverClassId(kInt32x4Cid)); | |
2422 ASSERT(call->ic_data()->HasOneTarget()); | |
2423 return InlineInt32x4Getter(call, recognized_kind); | |
2424 | |
2425 case MethodRecognizer::kInt32x4Select: { | |
2426 Definition* mask = call->ArgumentAt(0); | |
2427 Definition* trueValue = call->ArgumentAt(1); | |
2428 Definition* falseValue = call->ArgumentAt(2); | |
2429 // Type check left. | |
2430 AddCheckClass(mask, | |
2431 ICData::ZoneHandle( | |
2432 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2433 call->deopt_id(), | |
2434 call->env(), | |
2435 call); | |
2436 Int32x4SelectInstr* select = new(Z) Int32x4SelectInstr( | |
2437 new(Z) Value(mask), | |
2438 new(Z) Value(trueValue), | |
2439 new(Z) Value(falseValue), | |
2440 call->deopt_id()); | |
2441 ReplaceCall(call, select); | |
2442 return true; | |
2443 } | |
2444 case MethodRecognizer::kInt32x4WithFlagX: | |
2445 case MethodRecognizer::kInt32x4WithFlagY: | |
2446 case MethodRecognizer::kInt32x4WithFlagZ: | |
2447 case MethodRecognizer::kInt32x4WithFlagW: { | |
2448 Definition* left = call->ArgumentAt(0); | |
2449 Definition* flag = call->ArgumentAt(1); | |
2450 // Type check left. | |
2451 AddCheckClass(left, | |
2452 ICData::ZoneHandle( | |
2453 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | |
2454 call->deopt_id(), | |
2455 call->env(), | |
2456 call); | |
2457 Int32x4SetFlagInstr* setFlag = new(Z) Int32x4SetFlagInstr( | |
2458 recognized_kind, | |
2459 new(Z) Value(left), | |
2460 new(Z) Value(flag), | |
2461 call->deopt_id()); | |
2462 ReplaceCall(call, setFlag); | |
2463 return true; | |
2464 } | |
2465 default: | |
2466 return false; | |
2467 } | |
2468 } | 2097 } |
2469 | 2098 |
2470 | 2099 |
2471 // If type tests specified by 'ic_data' do not depend on type arguments, | 2100 // If type tests specified by 'ic_data' do not depend on type arguments, |
2472 // return mapping cid->result in 'results' (i : cid; i + 1: result). | 2101 // return mapping cid->result in 'results' (i : cid; i + 1: result). |
2473 // If all tests yield the same result, return it otherwise return Bool::null. | 2102 // If all tests yield the same result, return it otherwise return Bool::null. |
2474 // If no mapping is possible, 'results' is empty. | 2103 // If no mapping is possible, 'results' is empty. |
2475 // An instance-of test returning all same results can be converted to a class | 2104 // An instance-of test returning all same results can be converted to a class |
2476 // check. | 2105 // check. |
2477 RawBool* FlowGraphOptimizer::InstanceOfAsBool( | 2106 RawBool* AotOptimizer::InstanceOfAsBool( |
2478 const ICData& ic_data, | 2107 const ICData& ic_data, |
2479 const AbstractType& type, | 2108 const AbstractType& type, |
2480 ZoneGrowableArray<intptr_t>* results) const { | 2109 ZoneGrowableArray<intptr_t>* results) const { |
2481 ASSERT(results->is_empty()); | 2110 ASSERT(results->is_empty()); |
2482 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only. | 2111 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only. |
2483 if (type.IsFunctionType() || type.IsDartFunctionType() || | 2112 if (type.IsFunctionType() || type.IsDartFunctionType() || |
2484 !type.IsInstantiated() || type.IsMalformedOrMalbounded()) { | 2113 !type.IsInstantiated() || type.IsMalformedOrMalbounded()) { |
2485 return Bool::null(); | 2114 return Bool::null(); |
2486 } | 2115 } |
2487 const Class& type_class = Class::Handle(Z, type.type_class()); | 2116 const Class& type_class = Class::Handle(Z, type.type_class()); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2526 if (is_subtype != prev.value()) { | 2155 if (is_subtype != prev.value()) { |
2527 results_differ = true; | 2156 results_differ = true; |
2528 } | 2157 } |
2529 } | 2158 } |
2530 } | 2159 } |
2531 return results_differ ? Bool::null() : prev.raw(); | 2160 return results_differ ? Bool::null() : prev.raw(); |
2532 } | 2161 } |
2533 | 2162 |
2534 | 2163 |
2535 // Returns true if checking against this type is a direct class id comparison. | 2164 // Returns true if checking against this type is a direct class id comparison. |
2536 bool FlowGraphOptimizer::TypeCheckAsClassEquality(const AbstractType& type) { | 2165 bool AotOptimizer::TypeCheckAsClassEquality(const AbstractType& type) { |
2537 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); | 2166 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); |
2538 // Requires CHA. | 2167 // Requires CHA. |
2539 if (!type.IsInstantiated()) return false; | 2168 if (!type.IsInstantiated()) return false; |
2540 // Function types have different type checking rules. | 2169 // Function types have different type checking rules. |
2541 if (type.IsFunctionType()) return false; | 2170 if (type.IsFunctionType()) return false; |
2542 const Class& type_class = Class::Handle(type.type_class()); | 2171 const Class& type_class = Class::Handle(type.type_class()); |
2543 // Could be an interface check? | 2172 // Could be an interface check? |
2544 if (CHA::IsImplemented(type_class)) return false; | 2173 if (CHA::IsImplemented(type_class)) return false; |
2545 // Check if there are subclasses. | 2174 // Check if there are subclasses. |
2546 if (CHA::HasSubclasses(type_class)) { | 2175 if (CHA::HasSubclasses(type_class)) { |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2630 TryAddTest(results, kBigintCid, true); | 2259 TryAddTest(results, kBigintCid, true); |
2631 // Cannot deoptimize since all tests returning true have been added. | 2260 // Cannot deoptimize since all tests returning true have been added. |
2632 return false; | 2261 return false; |
2633 } | 2262 } |
2634 | 2263 |
2635 return true; // May deoptimize since we have not identified all 'true' tests. | 2264 return true; // May deoptimize since we have not identified all 'true' tests. |
2636 } | 2265 } |
2637 | 2266 |
2638 | 2267 |
2639 // TODO(srdjan): Use ICData to check if always true or false. | 2268 // TODO(srdjan): Use ICData to check if always true or false. |
2640 void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { | 2269 void AotOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { |
2641 ASSERT(Token::IsTypeTestOperator(call->token_kind())); | 2270 ASSERT(Token::IsTypeTestOperator(call->token_kind())); |
2642 Definition* left = call->ArgumentAt(0); | 2271 Definition* left = call->ArgumentAt(0); |
2643 Definition* type_args = NULL; | 2272 Definition* type_args = NULL; |
2644 AbstractType& type = AbstractType::ZoneHandle(Z); | 2273 AbstractType& type = AbstractType::ZoneHandle(Z); |
2645 bool negate = false; | 2274 bool negate = false; |
2646 if (call->ArgumentCount() == 2) { | 2275 if (call->ArgumentCount() == 2) { |
2647 type_args = flow_graph()->constant_null(); | 2276 type_args = flow_graph()->constant_null(); |
2648 if (call->function_name().raw() == | 2277 if (call->function_name().raw() == |
2649 Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) { | 2278 Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) { |
2650 type = Type::Number(); | 2279 type = Type::Number(); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2738 new(Z) Value(left), | 2367 new(Z) Value(left), |
2739 new(Z) Value(type_args), | 2368 new(Z) Value(type_args), |
2740 type, | 2369 type, |
2741 negate, | 2370 negate, |
2742 call->deopt_id()); | 2371 call->deopt_id()); |
2743 ReplaceCall(call, instance_of); | 2372 ReplaceCall(call, instance_of); |
2744 } | 2373 } |
2745 | 2374 |
2746 | 2375 |
2747 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). | 2376 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). |
2748 void FlowGraphOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { | 2377 void AotOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { |
2749 ASSERT(Token::IsTypeCastOperator(call->token_kind())); | 2378 ASSERT(Token::IsTypeCastOperator(call->token_kind())); |
2750 Definition* left = call->ArgumentAt(0); | 2379 Definition* left = call->ArgumentAt(0); |
2751 Definition* type_args = call->ArgumentAt(1); | 2380 Definition* type_args = call->ArgumentAt(1); |
2752 const AbstractType& type = | 2381 const AbstractType& type = |
2753 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()); | 2382 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()); |
2754 ASSERT(!type.IsMalformedOrMalbounded()); | 2383 ASSERT(!type.IsMalformedOrMalbounded()); |
2755 const ICData& unary_checks = | 2384 const ICData& unary_checks = |
2756 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); | 2385 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); |
2757 if ((unary_checks.NumberOfChecks() > 0) && | 2386 if ((unary_checks.NumberOfChecks() > 0) && |
2758 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { | 2387 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { |
(...skipping 22 matching lines...) Expand all Loading... | |
2781 new(Z) AssertAssignableInstr(call->token_pos(), | 2410 new(Z) AssertAssignableInstr(call->token_pos(), |
2782 new(Z) Value(left), | 2411 new(Z) Value(left), |
2783 new(Z) Value(type_args), | 2412 new(Z) Value(type_args), |
2784 type, | 2413 type, |
2785 dst_name, | 2414 dst_name, |
2786 call->deopt_id()); | 2415 call->deopt_id()); |
2787 ReplaceCall(call, assert_as); | 2416 ReplaceCall(call, assert_as); |
2788 } | 2417 } |
2789 | 2418 |
2790 | 2419 |
2791 bool FlowGraphOptimizer::IsBlackListedForInlining(intptr_t call_deopt_id) { | 2420 bool AotOptimizer::IsBlackListedForInlining(intptr_t call_deopt_id) { |
2792 for (intptr_t i = 0; i < inlining_black_list_->length(); ++i) { | 2421 for (intptr_t i = 0; i < inlining_black_list_->length(); ++i) { |
2793 if ((*inlining_black_list_)[i] == call_deopt_id) return true; | 2422 if ((*inlining_black_list_)[i] == call_deopt_id) return true; |
2794 } | 2423 } |
2795 return false; | 2424 return false; |
2796 } | 2425 } |
2797 | 2426 |
2798 // Special optimizations when running in --noopt mode. | 2427 // Special optimizations when running in --noopt mode. |
2799 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { | 2428 void AotOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { |
2800 // TODO(srdjan): Investigate other attempts, as they are not allowed to | 2429 // TODO(srdjan): Investigate other attempts, as they are not allowed to |
2801 // deoptimize. | 2430 // deoptimize. |
2802 | 2431 |
2803 // Type test is special as it always gets converted into inlined code. | 2432 // Type test is special as it always gets converted into inlined code. |
2804 const Token::Kind op_kind = instr->token_kind(); | 2433 const Token::Kind op_kind = instr->token_kind(); |
2805 if (Token::IsTypeTestOperator(op_kind)) { | 2434 if (Token::IsTypeTestOperator(op_kind)) { |
2806 ReplaceWithInstanceOf(instr); | 2435 ReplaceWithInstanceOf(instr); |
2807 return; | 2436 return; |
2808 } | 2437 } |
2809 if (Token::IsTypeCastOperator(op_kind)) { | 2438 if (Token::IsTypeCastOperator(op_kind)) { |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2928 new(Z) PolymorphicInstanceCallInstr(instr, ic_data, | 2557 new(Z) PolymorphicInstanceCallInstr(instr, ic_data, |
2929 /* with_checks = */ false); | 2558 /* with_checks = */ false); |
2930 instr->ReplaceWith(call, current_iterator()); | 2559 instr->ReplaceWith(call, current_iterator()); |
2931 return; | 2560 return; |
2932 } | 2561 } |
2933 } | 2562 } |
2934 | 2563 |
2935 | 2564 |
2936 // Tries to optimize instance call by replacing it with a faster instruction | 2565 // Tries to optimize instance call by replacing it with a faster instruction |
2937 // (e.g, binary op, field load, ..). | 2566 // (e.g, binary op, field load, ..). |
2938 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 2567 void AotOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
2939 if (FLAG_precompilation) { | 2568 ASSERT(FLAG_precompilation); |
2940 InstanceCallNoopt(instr); | 2569 InstanceCallNoopt(instr); |
2941 return; | |
2942 } | |
2943 | |
2944 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | |
2945 return; | |
2946 } | |
2947 const Token::Kind op_kind = instr->token_kind(); | |
2948 | |
2949 // Type test is special as it always gets converted into inlined code. | |
2950 if (Token::IsTypeTestOperator(op_kind)) { | |
2951 ReplaceWithInstanceOf(instr); | |
2952 return; | |
2953 } | |
2954 | |
2955 if (Token::IsTypeCastOperator(op_kind)) { | |
2956 ReplaceWithTypeCast(instr); | |
2957 return; | |
2958 } | |
2959 | |
2960 const ICData& unary_checks = | |
2961 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | |
2962 | |
2963 const intptr_t max_checks = (op_kind == Token::kEQ) | |
2964 ? FLAG_max_equality_polymorphic_checks | |
2965 : FLAG_max_polymorphic_checks; | |
2966 if ((unary_checks.NumberOfChecks() > max_checks) && | |
2967 InstanceCallNeedsClassCheck(instr, RawFunction::kRegularFunction)) { | |
2968 // Too many checks, it will be megamorphic which needs unary checks. | |
2969 instr->set_ic_data(&unary_checks); | |
2970 return; | |
2971 } | |
2972 | |
2973 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { | |
2974 return; | |
2975 } | |
2976 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { | |
2977 return; | |
2978 } | |
2979 | |
2980 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { | |
2981 return; | |
2982 } | |
2983 | |
2984 if (Token::IsRelationalOperator(op_kind) && | |
2985 TryReplaceWithRelationalOp(instr, op_kind)) { | |
2986 return; | |
2987 } | |
2988 | |
2989 if (Token::IsBinaryOperator(op_kind) && | |
2990 TryReplaceWithBinaryOp(instr, op_kind)) { | |
2991 return; | |
2992 } | |
2993 if (Token::IsUnaryOperator(op_kind) && | |
2994 TryReplaceWithUnaryOp(instr, op_kind)) { | |
2995 return; | |
2996 } | |
2997 if ((op_kind == Token::kGET) && TryInlineInstanceGetter(instr)) { | |
2998 return; | |
2999 } | |
3000 if ((op_kind == Token::kSET) && | |
3001 TryInlineInstanceSetter(instr, unary_checks)) { | |
3002 return; | |
3003 } | |
3004 if (TryInlineInstanceMethod(instr)) { | |
3005 return; | |
3006 } | |
3007 | |
3008 bool has_one_target = unary_checks.HasOneTarget(); | |
3009 | |
3010 if (has_one_target) { | |
3011 // Check if the single target is a polymorphic target, if it is, | |
3012 // we don't have one target. | |
3013 const Function& target = | |
3014 Function::Handle(Z, unary_checks.GetTargetAt(0)); | |
3015 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); | |
3016 has_one_target = !polymorphic_target; | |
3017 } | |
3018 | |
3019 if (has_one_target) { | |
3020 RawFunction::Kind function_kind = | |
3021 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); | |
3022 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { | |
3023 PolymorphicInstanceCallInstr* call = | |
3024 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
3025 /* call_with_checks = */ false); | |
3026 instr->ReplaceWith(call, current_iterator()); | |
3027 return; | |
3028 } | |
3029 } | |
3030 | |
3031 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { | |
3032 bool call_with_checks; | |
3033 if (has_one_target && FLAG_polymorphic_with_deopt) { | |
3034 // Type propagation has not run yet, we cannot eliminate the check. | |
3035 AddReceiverCheck(instr); | |
3036 // Call can still deoptimize, do not detach environment from instr. | |
3037 call_with_checks = false; | |
3038 } else { | |
3039 call_with_checks = true; | |
3040 } | |
3041 PolymorphicInstanceCallInstr* call = | |
3042 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
3043 call_with_checks); | |
3044 instr->ReplaceWith(call, current_iterator()); | |
3045 } | |
3046 } | 2570 } |
3047 | 2571 |
3048 | 2572 |
3049 void FlowGraphOptimizer::VisitStaticCall(StaticCallInstr* call) { | 2573 void AotOptimizer::VisitStaticCall(StaticCallInstr* call) { |
3050 if (!CanUnboxDouble()) { | 2574 if (!CanUnboxDouble()) { |
3051 return; | 2575 return; |
3052 } | 2576 } |
3053 MethodRecognizer::Kind recognized_kind = | 2577 MethodRecognizer::Kind recognized_kind = |
3054 MethodRecognizer::RecognizeKind(call->function()); | 2578 MethodRecognizer::RecognizeKind(call->function()); |
3055 MathUnaryInstr::MathUnaryKind unary_kind; | 2579 MathUnaryInstr::MathUnaryKind unary_kind; |
3056 switch (recognized_kind) { | 2580 switch (recognized_kind) { |
3057 case MethodRecognizer::kMathSqrt: | 2581 case MethodRecognizer::kMathSqrt: |
3058 unary_kind = MathUnaryInstr::kSqrt; | 2582 unary_kind = MathUnaryInstr::kSqrt; |
3059 break; | 2583 break; |
3060 case MethodRecognizer::kMathSin: | 2584 case MethodRecognizer::kMathSin: |
3061 unary_kind = MathUnaryInstr::kSin; | 2585 unary_kind = MathUnaryInstr::kSin; |
3062 break; | 2586 break; |
3063 case MethodRecognizer::kMathCos: | 2587 case MethodRecognizer::kMathCos: |
3064 unary_kind = MathUnaryInstr::kCos; | 2588 unary_kind = MathUnaryInstr::kCos; |
3065 break; | 2589 break; |
3066 default: | 2590 default: |
3067 unary_kind = MathUnaryInstr::kIllegal; | 2591 unary_kind = MathUnaryInstr::kIllegal; |
3068 break; | 2592 break; |
3069 } | 2593 } |
3070 if (unary_kind != MathUnaryInstr::kIllegal) { | 2594 if (unary_kind != MathUnaryInstr::kIllegal) { |
3071 if (FLAG_precompilation) { | 2595 ASSERT(FLAG_precompilation); |
3072 // TODO(srdjan): Adapt MathUnaryInstr to allow tagged inputs as well. | 2596 // TODO(srdjan): Adapt MathUnaryInstr to allow tagged inputs as well. |
3073 return; | |
3074 } | |
3075 MathUnaryInstr* math_unary = | |
3076 new(Z) MathUnaryInstr(unary_kind, | |
3077 new(Z) Value(call->ArgumentAt(0)), | |
3078 call->deopt_id()); | |
3079 ReplaceCall(call, math_unary); | |
3080 return; | 2597 return; |
3081 } | 2598 } |
2599 | |
3082 switch (recognized_kind) { | 2600 switch (recognized_kind) { |
3083 case MethodRecognizer::kFloat32x4Zero: | 2601 case MethodRecognizer::kFloat32x4Zero: |
3084 case MethodRecognizer::kFloat32x4Splat: | 2602 case MethodRecognizer::kFloat32x4Splat: |
3085 case MethodRecognizer::kFloat32x4Constructor: | 2603 case MethodRecognizer::kFloat32x4Constructor: |
3086 case MethodRecognizer::kFloat32x4FromFloat64x2: | 2604 case MethodRecognizer::kFloat32x4FromFloat64x2: |
3087 TryInlineFloat32x4Constructor(call, recognized_kind); | 2605 TryInlineFloat32x4Constructor(call, recognized_kind); |
3088 break; | 2606 break; |
3089 case MethodRecognizer::kFloat64x2Constructor: | 2607 case MethodRecognizer::kFloat64x2Constructor: |
3090 case MethodRecognizer::kFloat64x2Zero: | 2608 case MethodRecognizer::kFloat64x2Zero: |
3091 case MethodRecognizer::kFloat64x2Splat: | 2609 case MethodRecognizer::kFloat64x2Splat: |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3147 } | 2665 } |
3148 } | 2666 } |
3149 break; | 2667 break; |
3150 } | 2668 } |
3151 case MethodRecognizer::kMathDoublePow: | 2669 case MethodRecognizer::kMathDoublePow: |
3152 case MethodRecognizer::kMathTan: | 2670 case MethodRecognizer::kMathTan: |
3153 case MethodRecognizer::kMathAsin: | 2671 case MethodRecognizer::kMathAsin: |
3154 case MethodRecognizer::kMathAcos: | 2672 case MethodRecognizer::kMathAcos: |
3155 case MethodRecognizer::kMathAtan: | 2673 case MethodRecognizer::kMathAtan: |
3156 case MethodRecognizer::kMathAtan2: { | 2674 case MethodRecognizer::kMathAtan2: { |
3157 if (FLAG_precompilation) { | 2675 ASSERT(FLAG_precompilation); |
3158 // No UnboxDouble instructons allowed. | 2676 // No UnboxDouble instructions allowed. |
3159 return; | 2677 return; |
3160 } | |
3161 // InvokeMathCFunctionInstr requires unboxed doubles. UnboxDouble | |
3162 // instructions contain type checks and conversions to double. | |
3163 ZoneGrowableArray<Value*>* args = | |
3164 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount()); | |
3165 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { | |
3166 args->Add(new(Z) Value(call->ArgumentAt(i))); | |
3167 } | |
3168 InvokeMathCFunctionInstr* invoke = | |
3169 new(Z) InvokeMathCFunctionInstr(args, | |
3170 call->deopt_id(), | |
3171 recognized_kind, | |
3172 call->token_pos()); | |
3173 ReplaceCall(call, invoke); | |
3174 break; | |
3175 } | 2678 } |
3176 case MethodRecognizer::kDoubleFromInteger: { | 2679 case MethodRecognizer::kDoubleFromInteger: { |
3177 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) { | 2680 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) { |
3178 const ICData& ic_data = *call->ic_data(); | 2681 const ICData& ic_data = *call->ic_data(); |
3179 if (CanUnboxDouble()) { | 2682 if (CanUnboxDouble()) { |
3180 if (ArgIsAlways(kSmiCid, ic_data, 1)) { | 2683 if (ArgIsAlways(kSmiCid, ic_data, 1)) { |
3181 Definition* arg = call->ArgumentAt(1); | 2684 Definition* arg = call->ArgumentAt(1); |
3182 AddCheckSmi(arg, call->deopt_id(), call->env(), call); | 2685 AddCheckSmi(arg, call->deopt_id(), call->env(), call); |
3183 ReplaceCall(call, | 2686 ReplaceCall(call, |
3184 new(Z) SmiToDoubleInstr(new(Z) Value(arg), | 2687 new(Z) SmiToDoubleInstr(new(Z) Value(arg), |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3220 default: | 2723 default: |
3221 break; | 2724 break; |
3222 } | 2725 } |
3223 } | 2726 } |
3224 } | 2727 } |
3225 } | 2728 } |
3226 } | 2729 } |
3227 } | 2730 } |
3228 | 2731 |
3229 | 2732 |
3230 void FlowGraphOptimizer::VisitStoreInstanceField( | 2733 void AotOptimizer::VisitAllocateContext(AllocateContextInstr* instr) { |
3231 StoreInstanceFieldInstr* instr) { | |
3232 if (instr->IsUnboxedStore()) { | |
3233 ASSERT(instr->is_potential_unboxed_initialization_); | |
3234 // Determine if this field should be unboxed based on the usage of getter | |
3235 // and setter functions: The heuristic requires that the setter has a | |
3236 // usage count of at least 1/kGetterSetterRatio of the getter usage count. | |
3237 // This is to avoid unboxing fields where the setter is never or rarely | |
3238 // executed. | |
3239 const Field& field = Field::ZoneHandle(Z, instr->field().raw()); | |
3240 const String& field_name = String::Handle(Z, field.name()); | |
3241 const Class& owner = Class::Handle(Z, field.owner()); | |
3242 const Function& getter = | |
3243 Function::Handle(Z, owner.LookupGetterFunction(field_name)); | |
3244 const Function& setter = | |
3245 Function::Handle(Z, owner.LookupSetterFunction(field_name)); | |
3246 bool unboxed_field = false; | |
3247 if (!getter.IsNull() && !setter.IsNull()) { | |
3248 if (field.is_double_initialized()) { | |
3249 unboxed_field = true; | |
3250 } else if ((setter.usage_counter() > 0) && | |
3251 ((FLAG_getter_setter_ratio * setter.usage_counter()) >= | |
3252 getter.usage_counter())) { | |
3253 unboxed_field = true; | |
3254 } | |
3255 } | |
3256 if (!unboxed_field) { | |
3257 // TODO(srdjan): Instead of aborting pass this field to the mutator thread | |
3258 // so that it can: | |
3259 // - set it to unboxed | |
3260 // - deoptimize dependent code. | |
3261 if (Compiler::IsBackgroundCompilation()) { | |
3262 isolate()->AddDeoptimizingBoxedField(field); | |
3263 Compiler::AbortBackgroundCompilation(Thread::kNoDeoptId); | |
3264 UNREACHABLE(); | |
3265 } | |
3266 if (FLAG_trace_optimization || FLAG_trace_field_guards) { | |
3267 THR_Print("Disabling unboxing of %s\n", field.ToCString()); | |
3268 if (!setter.IsNull()) { | |
3269 OS::Print(" setter usage count: %" Pd "\n", setter.usage_counter()); | |
3270 } | |
3271 if (!getter.IsNull()) { | |
3272 OS::Print(" getter usage count: %" Pd "\n", getter.usage_counter()); | |
3273 } | |
3274 } | |
3275 field.set_is_unboxing_candidate(false); | |
3276 field.DeoptimizeDependentCode(); | |
3277 } else { | |
3278 FlowGraph::AddToGuardedFields(flow_graph_->guarded_fields(), &field); | |
3279 } | |
3280 } | |
3281 } | |
3282 | |
3283 | |
3284 void FlowGraphOptimizer::VisitAllocateContext(AllocateContextInstr* instr) { | |
3285 // Replace generic allocation with a sequence of inlined allocation and | 2734 // Replace generic allocation with a sequence of inlined allocation and |
3286 // explicit initalizing stores. | 2735 // explicit initalizing stores. |
3287 AllocateUninitializedContextInstr* replacement = | 2736 AllocateUninitializedContextInstr* replacement = |
3288 new AllocateUninitializedContextInstr(instr->token_pos(), | 2737 new AllocateUninitializedContextInstr(instr->token_pos(), |
3289 instr->num_context_variables()); | 2738 instr->num_context_variables()); |
3290 instr->ReplaceWith(replacement, current_iterator()); | 2739 instr->ReplaceWith(replacement, current_iterator()); |
3291 | 2740 |
3292 StoreInstanceFieldInstr* store = | 2741 StoreInstanceFieldInstr* store = |
3293 new(Z) StoreInstanceFieldInstr(Context::parent_offset(), | 2742 new(Z) StoreInstanceFieldInstr(Context::parent_offset(), |
3294 new Value(replacement), | 2743 new Value(replacement), |
(...skipping 14 matching lines...) Expand all Loading... | |
3309 instr->token_pos()); | 2758 instr->token_pos()); |
3310 // Storing into uninitialized memory; remember to prevent dead store | 2759 // Storing into uninitialized memory; remember to prevent dead store |
3311 // elimination and ensure proper GC barrier. | 2760 // elimination and ensure proper GC barrier. |
3312 store->set_is_object_reference_initialization(true); | 2761 store->set_is_object_reference_initialization(true); |
3313 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect); | 2762 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect); |
3314 cursor = store; | 2763 cursor = store; |
3315 } | 2764 } |
3316 } | 2765 } |
3317 | 2766 |
3318 | 2767 |
3319 void FlowGraphOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { | 2768 void AotOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { |
3320 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. | 2769 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. |
3321 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) | 2770 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) |
3322 if (!instr->can_pack_into_smi()) | 2771 if (!instr->can_pack_into_smi()) |
3323 instr->set_representation(kUnboxedMint); | 2772 instr->set_representation(kUnboxedMint); |
3324 #endif | 2773 #endif |
3325 } | 2774 } |
3326 | 2775 |
3327 | 2776 |
3328 bool FlowGraphOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, | 2777 bool AotOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, |
3329 const ICData& unary_ic_data, | 2778 const ICData& unary_ic_data, |
3330 bool allow_checks) { | 2779 bool allow_checks) { |
3331 ASSERT((unary_ic_data.NumberOfChecks() > 0) && | 2780 ASSERT((unary_ic_data.NumberOfChecks() > 0) && |
3332 (unary_ic_data.NumArgsTested() == 1)); | 2781 (unary_ic_data.NumArgsTested() == 1)); |
3333 if (I->flags().type_checks()) { | 2782 if (I->flags().type_checks()) { |
3334 // Checked mode setters are inlined like normal methods by conventional | 2783 // Checked mode setters are inlined like normal methods by conventional |
3335 // inlining. | 2784 // inlining. |
3336 return false; | 2785 return false; |
3337 } | 2786 } |
3338 | 2787 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3406 | 2855 |
3407 // Discard the environment from the original instruction because the store | 2856 // Discard the environment from the original instruction because the store |
3408 // can't deoptimize. | 2857 // can't deoptimize. |
3409 instr->RemoveEnvironment(); | 2858 instr->RemoveEnvironment(); |
3410 ReplaceCall(instr, store); | 2859 ReplaceCall(instr, store); |
3411 return true; | 2860 return true; |
3412 } | 2861 } |
3413 | 2862 |
3414 | 2863 |
3415 } // namespace dart | 2864 } // namespace dart |
OLD | NEW |