Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: runtime/vm/flow_graph_optimizer.cc

Issue 1732483002: Rename FlowGraphOptimizer -> JitOptimizer, clean up optimizer code. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "vm/flow_graph_optimizer.h"
6
7 #include "vm/bit_vector.h"
8 #include "vm/branch_optimizer.h"
9 #include "vm/cha.h"
10 #include "vm/compiler.h"
11 #include "vm/cpu.h"
12 #include "vm/dart_entry.h"
13 #include "vm/exceptions.h"
14 #include "vm/flow_graph_builder.h"
15 #include "vm/flow_graph_compiler.h"
16 #include "vm/flow_graph_inliner.h"
17 #include "vm/flow_graph_range_analysis.h"
18 #include "vm/hash_map.h"
19 #include "vm/il_printer.h"
20 #include "vm/intermediate_language.h"
21 #include "vm/object_store.h"
22 #include "vm/parser.h"
23 #include "vm/resolver.h"
24 #include "vm/scopes.h"
25 #include "vm/stack_frame.h"
26 #include "vm/symbols.h"
27
28 namespace dart {
29
30 // Quick access to the current isolate and zone.
31 #define I (isolate())
32 #define Z (zone())
33
34 static bool ShouldInlineSimd() {
35 return FlowGraphCompiler::SupportsUnboxedSimd128();
36 }
37
38
39 static bool CanUnboxDouble() {
40 return FlowGraphCompiler::SupportsUnboxedDoubles();
41 }
42
43
44 static bool CanConvertUnboxedMintToDouble() {
45 return FlowGraphCompiler::CanConvertUnboxedMintToDouble();
46 }
47
48
49 // Optimize instance calls using ICData.
50 void FlowGraphOptimizer::ApplyICData() {
51 VisitBlocks();
52 }
53
54
55 // Optimize instance calls using cid. This is called after optimizer
56 // converted instance calls to instructions. Any remaining
57 // instance calls are either megamorphic calls, cannot be optimized or
58 // have no runtime type feedback collected.
59 // Attempts to convert an instance call (IC call) using propagated class-ids,
60 // e.g., receiver class id, guarded-cid, or by guessing cid-s.
61 void FlowGraphOptimizer::ApplyClassIds() {
62 ASSERT(current_iterator_ == NULL);
63 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
64 !block_it.Done();
65 block_it.Advance()) {
66 ForwardInstructionIterator it(block_it.Current());
67 current_iterator_ = ⁢
68 for (; !it.Done(); it.Advance()) {
69 Instruction* instr = it.Current();
70 if (instr->IsInstanceCall()) {
71 InstanceCallInstr* call = instr->AsInstanceCall();
72 if (call->HasICData()) {
73 if (TryCreateICData(call)) {
74 VisitInstanceCall(call);
75 }
76 }
77 } else if (instr->IsPolymorphicInstanceCall()) {
78 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall());
79 }
80 }
81 current_iterator_ = NULL;
82 }
83 }
84
85
86 // TODO(srdjan): Test/support other number types as well.
87 static bool IsNumberCid(intptr_t cid) {
88 return (cid == kSmiCid) || (cid == kDoubleCid);
89 }
90
91
92 bool FlowGraphOptimizer::TryCreateICData(InstanceCallInstr* call) {
93 ASSERT(call->HasICData());
94 if (call->ic_data()->NumberOfUsedChecks() > 0) {
95 // This occurs when an instance call has too many checks, will be converted
96 // to megamorphic call.
97 return false;
98 }
99 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested());
100 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount());
101 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) {
102 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid());
103 }
104
105 const Token::Kind op_kind = call->token_kind();
106 if (Token::IsRelationalOperator(op_kind) ||
107 Token::IsEqualityOperator(op_kind) ||
108 Token::IsBinaryOperator(op_kind)) {
109 // Guess cid: if one of the inputs is a number assume that the other
110 // is a number of same type.
111 if (FLAG_guess_icdata_cid) {
112 const intptr_t cid_0 = class_ids[0];
113 const intptr_t cid_1 = class_ids[1];
114 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) {
115 class_ids[0] = cid_1;
116 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) {
117 class_ids[1] = cid_0;
118 }
119 }
120 }
121
122 bool all_cids_known = true;
123 for (intptr_t i = 0; i < class_ids.length(); i++) {
124 if (class_ids[i] == kDynamicCid) {
125 // Not all cid-s known.
126 all_cids_known = false;
127 break;
128 }
129 }
130
131 if (all_cids_known) {
132 const Class& receiver_class = Class::Handle(Z,
133 isolate()->class_table()->At(class_ids[0]));
134 if (!receiver_class.is_finalized()) {
135 // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can
136 // cause class finalization, since callee's receiver class may not be
137 // finalized yet.
138 return false;
139 }
140 const Array& args_desc_array = Array::Handle(Z,
141 ArgumentsDescriptor::New(call->ArgumentCount(),
142 call->argument_names()));
143 ArgumentsDescriptor args_desc(args_desc_array);
144 const Function& function = Function::Handle(Z,
145 Resolver::ResolveDynamicForReceiverClass(
146 receiver_class,
147 call->function_name(),
148 args_desc,
149 false /* allow add */));
150 if (function.IsNull()) {
151 return false;
152 }
153
154 // Create new ICData, do not modify the one attached to the instruction
155 // since it is attached to the assembly instruction itself.
156 // TODO(srdjan): Prevent modification of ICData object that is
157 // referenced in assembly code.
158 const ICData& ic_data = ICData::ZoneHandle(Z,
159 ICData::NewFrom(*call->ic_data(), class_ids.length()));
160 if (class_ids.length() > 1) {
161 ic_data.AddCheck(class_ids, function);
162 } else {
163 ASSERT(class_ids.length() == 1);
164 ic_data.AddReceiverCheck(class_ids[0], function);
165 }
166 call->set_ic_data(&ic_data);
167 return true;
168 }
169
170 // Check if getter or setter in function's class and class is currently leaf.
171 if (FLAG_guess_icdata_cid &&
172 ((call->token_kind() == Token::kGET) ||
173 (call->token_kind() == Token::kSET))) {
174 const Class& owner_class = Class::Handle(Z, function().Owner());
175 if (!owner_class.is_abstract() &&
176 !CHA::HasSubclasses(owner_class) &&
177 !CHA::IsImplemented(owner_class)) {
178 const Array& args_desc_array = Array::Handle(Z,
179 ArgumentsDescriptor::New(call->ArgumentCount(),
180 call->argument_names()));
181 ArgumentsDescriptor args_desc(args_desc_array);
182 const Function& function = Function::Handle(Z,
183 Resolver::ResolveDynamicForReceiverClass(owner_class,
184 call->function_name(),
185 args_desc,
186 false /* allow_add */));
187 if (!function.IsNull()) {
188 const ICData& ic_data = ICData::ZoneHandle(Z,
189 ICData::NewFrom(*call->ic_data(), class_ids.length()));
190 ic_data.AddReceiverCheck(owner_class.id(), function);
191 call->set_ic_data(&ic_data);
192 return true;
193 }
194 }
195 }
196
197 return false;
198 }
199
200
201 const ICData& FlowGraphOptimizer::TrySpecializeICData(const ICData& ic_data,
202 intptr_t cid) {
203 ASSERT(ic_data.NumArgsTested() == 1);
204
205 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) {
206 return ic_data; // Nothing to do
207 }
208
209 const Function& function =
210 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid));
211 // TODO(fschneider): Try looking up the function on the class if it is
212 // not found in the ICData.
213 if (!function.IsNull()) {
214 const ICData& new_ic_data = ICData::ZoneHandle(Z, ICData::New(
215 Function::Handle(Z, ic_data.Owner()),
216 String::Handle(Z, ic_data.target_name()),
217 Object::empty_array(), // Dummy argument descriptor.
218 ic_data.deopt_id(),
219 ic_data.NumArgsTested()));
220 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons());
221 new_ic_data.AddReceiverCheck(cid, function);
222 return new_ic_data;
223 }
224
225 return ic_data;
226 }
227
228
229 void FlowGraphOptimizer::SpecializePolymorphicInstanceCall(
230 PolymorphicInstanceCallInstr* call) {
231 if (!FLAG_polymorphic_with_deopt) {
232 // Specialization adds receiver checks which can lead to deoptimization.
233 return;
234 }
235 if (!call->with_checks()) {
236 return; // Already specialized.
237 }
238
239 const intptr_t receiver_cid =
240 call->PushArgumentAt(0)->value()->Type()->ToCid();
241 if (receiver_cid == kDynamicCid) {
242 return; // No information about receiver was infered.
243 }
244
245 const ICData& ic_data = TrySpecializeICData(call->ic_data(), receiver_cid);
246 if (ic_data.raw() == call->ic_data().raw()) {
247 // No specialization.
248 return;
249 }
250
251 const bool with_checks = false;
252 PolymorphicInstanceCallInstr* specialized =
253 new(Z) PolymorphicInstanceCallInstr(call->instance_call(),
254 ic_data,
255 with_checks);
256 call->ReplaceWith(specialized, current_iterator());
257 }
258
259
260 static BinarySmiOpInstr* AsSmiShiftLeftInstruction(Definition* d) {
261 BinarySmiOpInstr* instr = d->AsBinarySmiOp();
262 if ((instr != NULL) && (instr->op_kind() == Token::kSHL)) {
263 return instr;
264 }
265 return NULL;
266 }
267
268
269 static bool IsPositiveOrZeroSmiConst(Definition* d) {
270 ConstantInstr* const_instr = d->AsConstant();
271 if ((const_instr != NULL) && (const_instr->value().IsSmi())) {
272 return Smi::Cast(const_instr->value()).Value() >= 0;
273 }
274 return false;
275 }
276
277
278 void FlowGraphOptimizer::OptimizeLeftShiftBitAndSmiOp(
279 Definition* bit_and_instr,
280 Definition* left_instr,
281 Definition* right_instr) {
282 ASSERT(bit_and_instr != NULL);
283 ASSERT((left_instr != NULL) && (right_instr != NULL));
284
285 // Check for pattern, smi_shift_left must be single-use.
286 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr);
287 if (!is_positive_or_zero) {
288 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr);
289 }
290 if (!is_positive_or_zero) return;
291
292 BinarySmiOpInstr* smi_shift_left = NULL;
293 if (bit_and_instr->InputAt(0)->IsSingleUse()) {
294 smi_shift_left = AsSmiShiftLeftInstruction(left_instr);
295 }
296 if ((smi_shift_left == NULL) && (bit_and_instr->InputAt(1)->IsSingleUse())) {
297 smi_shift_left = AsSmiShiftLeftInstruction(right_instr);
298 }
299 if (smi_shift_left == NULL) return;
300
301 // Pattern recognized.
302 smi_shift_left->mark_truncating();
303 ASSERT(bit_and_instr->IsBinarySmiOp() || bit_and_instr->IsBinaryMintOp());
304 if (bit_and_instr->IsBinaryMintOp()) {
305 // Replace Mint op with Smi op.
306 BinarySmiOpInstr* smi_op = new(Z) BinarySmiOpInstr(
307 Token::kBIT_AND,
308 new(Z) Value(left_instr),
309 new(Z) Value(right_instr),
310 Thread::kNoDeoptId); // BIT_AND cannot deoptimize.
311 bit_and_instr->ReplaceWith(smi_op, current_iterator());
312 }
313 }
314
315
316 void FlowGraphOptimizer::AppendExtractNthOutputForMerged(Definition* instr,
317 intptr_t index,
318 Representation rep,
319 intptr_t cid) {
320 ExtractNthOutputInstr* extract =
321 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid);
322 instr->ReplaceUsesWith(extract);
323 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue);
324 }
325
326
327 // Dart:
328 // var x = d % 10;
329 // var y = d ~/ 10;
330 // var z = x + y;
331 //
332 // IL:
333 // v4 <- %(v2, v3)
334 // v5 <- ~/(v2, v3)
335 // v6 <- +(v4, v5)
336 //
337 // IL optimized:
338 // v4 <- DIVMOD(v2, v3);
339 // v5 <- LoadIndexed(v4, 0); // ~/ result
340 // v6 <- LoadIndexed(v4, 1); // % result
341 // v7 <- +(v5, v6)
342 // Because of the environment it is important that merged instruction replaces
343 // first original instruction encountered.
344 void FlowGraphOptimizer::TryMergeTruncDivMod(
345 GrowableArray<BinarySmiOpInstr*>* merge_candidates) {
346 if (merge_candidates->length() < 2) {
347 // Need at least a TRUNCDIV and a MOD.
348 return;
349 }
350 for (intptr_t i = 0; i < merge_candidates->length(); i++) {
351 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i];
352 if (curr_instr == NULL) {
353 // Instruction was merged already.
354 continue;
355 }
356 ASSERT((curr_instr->op_kind() == Token::kTRUNCDIV) ||
357 (curr_instr->op_kind() == Token::kMOD));
358 // Check if there is kMOD/kTRUNDIV binop with same inputs.
359 const intptr_t other_kind = (curr_instr->op_kind() == Token::kTRUNCDIV) ?
360 Token::kMOD : Token::kTRUNCDIV;
361 Definition* left_def = curr_instr->left()->definition();
362 Definition* right_def = curr_instr->right()->definition();
363 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) {
364 BinarySmiOpInstr* other_binop = (*merge_candidates)[k];
365 // 'other_binop' can be NULL if it was already merged.
366 if ((other_binop != NULL) &&
367 (other_binop->op_kind() == other_kind) &&
368 (other_binop->left()->definition() == left_def) &&
369 (other_binop->right()->definition() == right_def)) {
370 (*merge_candidates)[k] = NULL; // Clear it.
371 ASSERT(curr_instr->HasUses());
372 AppendExtractNthOutputForMerged(
373 curr_instr,
374 MergedMathInstr::OutputIndexOf(curr_instr->op_kind()),
375 kTagged, kSmiCid);
376 ASSERT(other_binop->HasUses());
377 AppendExtractNthOutputForMerged(
378 other_binop,
379 MergedMathInstr::OutputIndexOf(other_binop->op_kind()),
380 kTagged, kSmiCid);
381
382 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(2);
383 args->Add(new(Z) Value(curr_instr->left()->definition()));
384 args->Add(new(Z) Value(curr_instr->right()->definition()));
385
386 // Replace with TruncDivMod.
387 MergedMathInstr* div_mod = new(Z) MergedMathInstr(
388 args,
389 curr_instr->deopt_id(),
390 MergedMathInstr::kTruncDivMod);
391 curr_instr->ReplaceWith(div_mod, current_iterator());
392 other_binop->ReplaceUsesWith(div_mod);
393 other_binop->RemoveFromGraph();
394 // Only one merge possible. Because canonicalization happens later,
395 // more candidates are possible.
396 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod.
397 break;
398 }
399 }
400 }
401 }
402
403
404 // Tries to merge MathUnary operations, in this case sinus and cosinus.
405 void FlowGraphOptimizer::TryMergeMathUnary(
406 GrowableArray<MathUnaryInstr*>* merge_candidates) {
407 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble() ||
408 !FLAG_merge_sin_cos) {
409 return;
410 }
411 if (merge_candidates->length() < 2) {
412 // Need at least a SIN and a COS.
413 return;
414 }
415 for (intptr_t i = 0; i < merge_candidates->length(); i++) {
416 MathUnaryInstr* curr_instr = (*merge_candidates)[i];
417 if (curr_instr == NULL) {
418 // Instruction was merged already.
419 continue;
420 }
421 const intptr_t kind = curr_instr->kind();
422 ASSERT((kind == MathUnaryInstr::kSin) ||
423 (kind == MathUnaryInstr::kCos));
424 // Check if there is sin/cos binop with same inputs.
425 const intptr_t other_kind = (kind == MathUnaryInstr::kSin) ?
426 MathUnaryInstr::kCos : MathUnaryInstr::kSin;
427 Definition* def = curr_instr->value()->definition();
428 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) {
429 MathUnaryInstr* other_op = (*merge_candidates)[k];
430 // 'other_op' can be NULL if it was already merged.
431 if ((other_op != NULL) && (other_op->kind() == other_kind) &&
432 (other_op->value()->definition() == def)) {
433 (*merge_candidates)[k] = NULL; // Clear it.
434 ASSERT(curr_instr->HasUses());
435 AppendExtractNthOutputForMerged(curr_instr,
436 MergedMathInstr::OutputIndexOf(kind),
437 kUnboxedDouble, kDoubleCid);
438 ASSERT(other_op->HasUses());
439 AppendExtractNthOutputForMerged(
440 other_op,
441 MergedMathInstr::OutputIndexOf(other_kind),
442 kUnboxedDouble, kDoubleCid);
443 ZoneGrowableArray<Value*>* args = new(Z) ZoneGrowableArray<Value*>(1);
444 args->Add(new(Z) Value(curr_instr->value()->definition()));
445 // Replace with SinCos.
446 MergedMathInstr* sin_cos =
447 new(Z) MergedMathInstr(args,
448 curr_instr->DeoptimizationTarget(),
449 MergedMathInstr::kSinCos);
450 curr_instr->ReplaceWith(sin_cos, current_iterator());
451 other_op->ReplaceUsesWith(sin_cos);
452 other_op->RemoveFromGraph();
453 // Only one merge possible. Because canonicalization happens later,
454 // more candidates are possible.
455 // TODO(srdjan): Allow merging of sin/cos into sincos.
456 break;
457 }
458 }
459 }
460 }
461
462
463 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the
464 // shift can be a truncating Smi shift-left and result is always Smi.
465 // Merging occurs only per basic-block.
466 void FlowGraphOptimizer::TryOptimizePatterns() {
467 if (!FLAG_truncating_left_shift) return;
468 ASSERT(current_iterator_ == NULL);
469 GrowableArray<BinarySmiOpInstr*> div_mod_merge;
470 GrowableArray<MathUnaryInstr*> sin_cos_merge;
471 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
472 !block_it.Done();
473 block_it.Advance()) {
474 // Merging only per basic-block.
475 div_mod_merge.Clear();
476 sin_cos_merge.Clear();
477 ForwardInstructionIterator it(block_it.Current());
478 current_iterator_ = &it;
479 for (; !it.Done(); it.Advance()) {
480 if (it.Current()->IsBinarySmiOp()) {
481 BinarySmiOpInstr* binop = it.Current()->AsBinarySmiOp();
482 if (binop->op_kind() == Token::kBIT_AND) {
483 OptimizeLeftShiftBitAndSmiOp(binop,
484 binop->left()->definition(),
485 binop->right()->definition());
486 } else if ((binop->op_kind() == Token::kTRUNCDIV) ||
487 (binop->op_kind() == Token::kMOD)) {
488 if (binop->HasUses()) {
489 div_mod_merge.Add(binop);
490 }
491 }
492 } else if (it.Current()->IsBinaryMintOp()) {
493 BinaryMintOpInstr* mintop = it.Current()->AsBinaryMintOp();
494 if (mintop->op_kind() == Token::kBIT_AND) {
495 OptimizeLeftShiftBitAndSmiOp(mintop,
496 mintop->left()->definition(),
497 mintop->right()->definition());
498 }
499 } else if (it.Current()->IsMathUnary()) {
500 MathUnaryInstr* math_unary = it.Current()->AsMathUnary();
501 if ((math_unary->kind() == MathUnaryInstr::kSin) ||
502 (math_unary->kind() == MathUnaryInstr::kCos)) {
503 if (math_unary->HasUses()) {
504 sin_cos_merge.Add(math_unary);
505 }
506 }
507 }
508 }
509 TryMergeTruncDivMod(&div_mod_merge);
510 TryMergeMathUnary(&sin_cos_merge);
511 current_iterator_ = NULL;
512 }
513 }
514
515
516 static bool ClassIdIsOneOf(intptr_t class_id,
517 const GrowableArray<intptr_t>& class_ids) {
518 for (intptr_t i = 0; i < class_ids.length(); i++) {
519 ASSERT(class_ids[i] != kIllegalCid);
520 if (class_ids[i] == class_id) {
521 return true;
522 }
523 }
524 return false;
525 }
526
527
528 // Returns true if ICData tests two arguments and all ICData cids are in the
529 // required sets 'receiver_class_ids' or 'argument_class_ids', respectively.
530 static bool ICDataHasOnlyReceiverArgumentClassIds(
531 const ICData& ic_data,
532 const GrowableArray<intptr_t>& receiver_class_ids,
533 const GrowableArray<intptr_t>& argument_class_ids) {
534 if (ic_data.NumArgsTested() != 2) {
535 return false;
536 }
537 const intptr_t len = ic_data.NumberOfChecks();
538 GrowableArray<intptr_t> class_ids;
539 for (intptr_t i = 0; i < len; i++) {
540 if (ic_data.IsUsedAt(i)) {
541 ic_data.GetClassIdsAt(i, &class_ids);
542 ASSERT(class_ids.length() == 2);
543 if (!ClassIdIsOneOf(class_ids[0], receiver_class_ids) ||
544 !ClassIdIsOneOf(class_ids[1], argument_class_ids)) {
545 return false;
546 }
547 }
548 }
549 return true;
550 }
551
552
553 static bool ICDataHasReceiverArgumentClassIds(const ICData& ic_data,
554 intptr_t receiver_class_id,
555 intptr_t argument_class_id) {
556 if (ic_data.NumArgsTested() != 2) {
557 return false;
558 }
559 const intptr_t len = ic_data.NumberOfChecks();
560 for (intptr_t i = 0; i < len; i++) {
561 if (ic_data.IsUsedAt(i)) {
562 GrowableArray<intptr_t> class_ids;
563 ic_data.GetClassIdsAt(i, &class_ids);
564 ASSERT(class_ids.length() == 2);
565 if ((class_ids[0] == receiver_class_id) &&
566 (class_ids[1] == argument_class_id)) {
567 return true;
568 }
569 }
570 }
571 return false;
572 }
573
574
575 static bool HasOnlyOneSmi(const ICData& ic_data) {
576 return (ic_data.NumberOfUsedChecks() == 1)
577 && ic_data.HasReceiverClassId(kSmiCid);
578 }
579
580
581 static bool HasOnlySmiOrMint(const ICData& ic_data) {
582 if (ic_data.NumberOfUsedChecks() == 1) {
583 return ic_data.HasReceiverClassId(kSmiCid)
584 || ic_data.HasReceiverClassId(kMintCid);
585 }
586 return (ic_data.NumberOfUsedChecks() == 2)
587 && ic_data.HasReceiverClassId(kSmiCid)
588 && ic_data.HasReceiverClassId(kMintCid);
589 }
590
591
592 static bool HasOnlyTwoOf(const ICData& ic_data, intptr_t cid) {
593 if (ic_data.NumberOfUsedChecks() != 1) {
594 return false;
595 }
596 GrowableArray<intptr_t> first;
597 GrowableArray<intptr_t> second;
598 ic_data.GetUsedCidsForTwoArgs(&first, &second);
599 return (first[0] == cid) && (second[0] == cid);
600 }
601
602 // Returns false if the ICData contains anything other than the 4 combinations
603 // of Mint and Smi for the receiver and argument classes.
604 static bool HasTwoMintOrSmi(const ICData& ic_data) {
605 GrowableArray<intptr_t> first;
606 GrowableArray<intptr_t> second;
607 ic_data.GetUsedCidsForTwoArgs(&first, &second);
608 for (intptr_t i = 0; i < first.length(); i++) {
609 if ((first[i] != kSmiCid) && (first[i] != kMintCid)) {
610 return false;
611 }
612 if ((second[i] != kSmiCid) && (second[i] != kMintCid)) {
613 return false;
614 }
615 }
616 return true;
617 }
618
619
620 // Returns false if the ICData contains anything other than the 4 combinations
621 // of Double and Smi for the receiver and argument classes.
622 static bool HasTwoDoubleOrSmi(const ICData& ic_data) {
623 GrowableArray<intptr_t> class_ids(2);
624 class_ids.Add(kSmiCid);
625 class_ids.Add(kDoubleCid);
626 return ICDataHasOnlyReceiverArgumentClassIds(ic_data, class_ids, class_ids);
627 }
628
629
630 static bool HasOnlyOneDouble(const ICData& ic_data) {
631 return (ic_data.NumberOfUsedChecks() == 1)
632 && ic_data.HasReceiverClassId(kDoubleCid);
633 }
634
635
636 static bool ShouldSpecializeForDouble(const ICData& ic_data) {
637 // Don't specialize for double if we can't unbox them.
638 if (!CanUnboxDouble()) {
639 return false;
640 }
641
642 // Unboxed double operation can't handle case of two smis.
643 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
644 return false;
645 }
646
647 // Check that it have seen only smis and doubles.
648 return HasTwoDoubleOrSmi(ic_data);
649 }
650
651
652 void FlowGraphOptimizer::ReplaceCall(Definition* call,
653 Definition* replacement) {
654 // Remove the original push arguments.
655 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
656 PushArgumentInstr* push = call->PushArgumentAt(i);
657 push->ReplaceUsesWith(push->value()->definition());
658 push->RemoveFromGraph();
659 }
660 call->ReplaceWith(replacement, current_iterator());
661 }
662
663
664 void FlowGraphOptimizer::AddCheckSmi(Definition* to_check,
665 intptr_t deopt_id,
666 Environment* deopt_environment,
667 Instruction* insert_before) {
668 if (to_check->Type()->ToCid() != kSmiCid) {
669 InsertBefore(insert_before,
670 new(Z) CheckSmiInstr(new(Z) Value(to_check),
671 deopt_id,
672 insert_before->token_pos()),
673 deopt_environment,
674 FlowGraph::kEffect);
675 }
676 }
677
678
679 Instruction* FlowGraphOptimizer::GetCheckClass(Definition* to_check,
680 const ICData& unary_checks,
681 intptr_t deopt_id,
682 TokenPosition token_pos) {
683 if ((unary_checks.NumberOfUsedChecks() == 1) &&
684 unary_checks.HasReceiverClassId(kSmiCid)) {
685 return new(Z) CheckSmiInstr(new(Z) Value(to_check),
686 deopt_id,
687 token_pos);
688 }
689 return new(Z) CheckClassInstr(
690 new(Z) Value(to_check), deopt_id, unary_checks, token_pos);
691 }
692
693
694 void FlowGraphOptimizer::AddCheckClass(Definition* to_check,
695 const ICData& unary_checks,
696 intptr_t deopt_id,
697 Environment* deopt_environment,
698 Instruction* insert_before) {
699 // Type propagation has not run yet, we cannot eliminate the check.
700 Instruction* check = GetCheckClass(
701 to_check, unary_checks, deopt_id, insert_before->token_pos());
702 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect);
703 }
704
705
706 void FlowGraphOptimizer::AddReceiverCheck(InstanceCallInstr* call) {
707 AddCheckClass(call->ArgumentAt(0),
708 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()),
709 call->deopt_id(),
710 call->env(),
711 call);
712 }
713
714
715 static bool ArgIsAlways(intptr_t cid,
716 const ICData& ic_data,
717 intptr_t arg_number) {
718 ASSERT(ic_data.NumArgsTested() > arg_number);
719 if (ic_data.NumberOfUsedChecks() == 0) {
720 return false;
721 }
722 const intptr_t num_checks = ic_data.NumberOfChecks();
723 for (intptr_t i = 0; i < num_checks; i++) {
724 if (ic_data.IsUsedAt(i) && ic_data.GetClassIdAt(i, arg_number) != cid) {
725 return false;
726 }
727 }
728 return true;
729 }
730
731
732 bool FlowGraphOptimizer::TryReplaceWithIndexedOp(InstanceCallInstr* call) {
733 // Check for monomorphic IC data.
734 if (!call->HasICData()) return false;
735 const ICData& ic_data =
736 ICData::Handle(Z, call->ic_data()->AsUnaryClassChecks());
737 if (ic_data.NumberOfChecks() != 1) {
738 return false;
739 }
740 return TryReplaceInstanceCallWithInline(call);
741 }
742
743
744 // Return true if d is a string of length one (a constant or result from
745 // from string-from-char-code instruction.
746 static bool IsLengthOneString(Definition* d) {
747 if (d->IsConstant()) {
748 const Object& obj = d->AsConstant()->value();
749 if (obj.IsString()) {
750 return String::Cast(obj).Length() == 1;
751 } else {
752 return false;
753 }
754 } else {
755 return d->IsStringFromCharCode();
756 }
757 }
758
759
760 // Returns true if the string comparison was converted into char-code
761 // comparison. Conversion is only possible for strings of length one.
762 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes.
763 // TODO(srdjan): Expand for two-byte and external strings.
764 bool FlowGraphOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call,
765 Token::Kind op_kind) {
766 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid));
767 // Check that left and right are length one strings (either string constants
768 // or results of string-from-char-code.
769 Definition* left = call->ArgumentAt(0);
770 Definition* right = call->ArgumentAt(1);
771 Value* left_val = NULL;
772 Definition* to_remove_left = NULL;
773 if (IsLengthOneString(right)) {
774 // Swap, since we know that both arguments are strings
775 Definition* temp = left;
776 left = right;
777 right = temp;
778 }
779 if (IsLengthOneString(left)) {
780 // Optimize if left is a string with length one (either constant or
781 // result of string-from-char-code.
782 if (left->IsConstant()) {
783 ConstantInstr* left_const = left->AsConstant();
784 const String& str = String::Cast(left_const->value());
785 ASSERT(str.Length() == 1);
786 ConstantInstr* char_code_left = flow_graph()->GetConstant(
787 Smi::ZoneHandle(Z, Smi::New(static_cast<intptr_t>(str.CharAt(0)))));
788 left_val = new(Z) Value(char_code_left);
789 } else if (left->IsStringFromCharCode()) {
790 // Use input of string-from-charcode as left value.
791 StringFromCharCodeInstr* instr = left->AsStringFromCharCode();
792 left_val = new(Z) Value(instr->char_code()->definition());
793 to_remove_left = instr;
794 } else {
795 // IsLengthOneString(left) should have been false.
796 UNREACHABLE();
797 }
798
799 Definition* to_remove_right = NULL;
800 Value* right_val = NULL;
801 if (right->IsStringFromCharCode()) {
802 // Skip string-from-char-code, and use its input as right value.
803 StringFromCharCodeInstr* right_instr = right->AsStringFromCharCode();
804 right_val = new(Z) Value(right_instr->char_code()->definition());
805 to_remove_right = right_instr;
806 } else {
807 const ICData& unary_checks_1 =
808 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1));
809 AddCheckClass(right,
810 unary_checks_1,
811 call->deopt_id(),
812 call->env(),
813 call);
814 // String-to-char-code instructions returns -1 (illegal charcode) if
815 // string is not of length one.
816 StringToCharCodeInstr* char_code_right =
817 new(Z) StringToCharCodeInstr(new(Z) Value(right), kOneByteStringCid);
818 InsertBefore(call, char_code_right, call->env(), FlowGraph::kValue);
819 right_val = new(Z) Value(char_code_right);
820 }
821
822 // Comparing char-codes instead of strings.
823 EqualityCompareInstr* comp =
824 new(Z) EqualityCompareInstr(call->token_pos(),
825 op_kind,
826 left_val,
827 right_val,
828 kSmiCid,
829 call->deopt_id());
830 ReplaceCall(call, comp);
831
832 // Remove dead instructions.
833 if ((to_remove_left != NULL) &&
834 (to_remove_left->input_use_list() == NULL)) {
835 to_remove_left->ReplaceUsesWith(flow_graph()->constant_null());
836 to_remove_left->RemoveFromGraph();
837 }
838 if ((to_remove_right != NULL) &&
839 (to_remove_right->input_use_list() == NULL)) {
840 to_remove_right->ReplaceUsesWith(flow_graph()->constant_null());
841 to_remove_right->RemoveFromGraph();
842 }
843 return true;
844 }
845 return false;
846 }
847
848
849 static bool SmiFitsInDouble() { return kSmiBits < 53; }
850
851 bool FlowGraphOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call,
852 Token::Kind op_kind) {
853 const ICData& ic_data = *call->ic_data();
854 ASSERT(ic_data.NumArgsTested() == 2);
855
856 ASSERT(call->ArgumentCount() == 2);
857 Definition* left = call->ArgumentAt(0);
858 Definition* right = call->ArgumentAt(1);
859
860 intptr_t cid = kIllegalCid;
861 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) {
862 if (TryStringLengthOneEquality(call, op_kind)) {
863 return true;
864 } else {
865 return false;
866 }
867 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) {
868 InsertBefore(call,
869 new(Z) CheckSmiInstr(new(Z) Value(left),
870 call->deopt_id(),
871 call->token_pos()),
872 call->env(),
873 FlowGraph::kEffect);
874 InsertBefore(call,
875 new(Z) CheckSmiInstr(new(Z) Value(right),
876 call->deopt_id(),
877 call->token_pos()),
878 call->env(),
879 FlowGraph::kEffect);
880 cid = kSmiCid;
881 } else if (HasTwoMintOrSmi(ic_data) &&
882 FlowGraphCompiler::SupportsUnboxedMints()) {
883 cid = kMintCid;
884 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) {
885 // Use double comparison.
886 if (SmiFitsInDouble()) {
887 cid = kDoubleCid;
888 } else {
889 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
890 // We cannot use double comparison on two smis. Need polymorphic
891 // call.
892 return false;
893 } else {
894 InsertBefore(call,
895 new(Z) CheckEitherNonSmiInstr(
896 new(Z) Value(left),
897 new(Z) Value(right),
898 call->deopt_id()),
899 call->env(),
900 FlowGraph::kEffect);
901 cid = kDoubleCid;
902 }
903 }
904 } else {
905 // Check if ICDData contains checks with Smi/Null combinations. In that case
906 // we can still emit the optimized Smi equality operation but need to add
907 // checks for null or Smi.
908 GrowableArray<intptr_t> smi_or_null(2);
909 smi_or_null.Add(kSmiCid);
910 smi_or_null.Add(kNullCid);
911 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data,
912 smi_or_null,
913 smi_or_null)) {
914 const ICData& unary_checks_0 =
915 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
916 AddCheckClass(left,
917 unary_checks_0,
918 call->deopt_id(),
919 call->env(),
920 call);
921
922 const ICData& unary_checks_1 =
923 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1));
924 AddCheckClass(right,
925 unary_checks_1,
926 call->deopt_id(),
927 call->env(),
928 call);
929 cid = kSmiCid;
930 } else {
931 // Shortcut for equality with null.
932 ConstantInstr* right_const = right->AsConstant();
933 ConstantInstr* left_const = left->AsConstant();
934 if ((right_const != NULL && right_const->value().IsNull()) ||
935 (left_const != NULL && left_const->value().IsNull())) {
936 StrictCompareInstr* comp =
937 new(Z) StrictCompareInstr(call->token_pos(),
938 Token::kEQ_STRICT,
939 new(Z) Value(left),
940 new(Z) Value(right),
941 false); // No number check.
942 ReplaceCall(call, comp);
943 return true;
944 }
945 return false;
946 }
947 }
948 ASSERT(cid != kIllegalCid);
949 EqualityCompareInstr* comp = new(Z) EqualityCompareInstr(call->token_pos(),
950 op_kind,
951 new(Z) Value(left),
952 new(Z) Value(right),
953 cid,
954 call->deopt_id());
955 ReplaceCall(call, comp);
956 return true;
957 }
958
959
960 bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call,
961 Token::Kind op_kind) {
962 const ICData& ic_data = *call->ic_data();
963 ASSERT(ic_data.NumArgsTested() == 2);
964
965 ASSERT(call->ArgumentCount() == 2);
966 Definition* left = call->ArgumentAt(0);
967 Definition* right = call->ArgumentAt(1);
968
969 intptr_t cid = kIllegalCid;
970 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
971 InsertBefore(call,
972 new(Z) CheckSmiInstr(new(Z) Value(left),
973 call->deopt_id(),
974 call->token_pos()),
975 call->env(),
976 FlowGraph::kEffect);
977 InsertBefore(call,
978 new(Z) CheckSmiInstr(new(Z) Value(right),
979 call->deopt_id(),
980 call->token_pos()),
981 call->env(),
982 FlowGraph::kEffect);
983 cid = kSmiCid;
984 } else if (HasTwoMintOrSmi(ic_data) &&
985 FlowGraphCompiler::SupportsUnboxedMints()) {
986 cid = kMintCid;
987 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) {
988 // Use double comparison.
989 if (SmiFitsInDouble()) {
990 cid = kDoubleCid;
991 } else {
992 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
993 // We cannot use double comparison on two smis. Need polymorphic
994 // call.
995 return false;
996 } else {
997 InsertBefore(call,
998 new(Z) CheckEitherNonSmiInstr(
999 new(Z) Value(left),
1000 new(Z) Value(right),
1001 call->deopt_id()),
1002 call->env(),
1003 FlowGraph::kEffect);
1004 cid = kDoubleCid;
1005 }
1006 }
1007 } else {
1008 return false;
1009 }
1010 ASSERT(cid != kIllegalCid);
1011 RelationalOpInstr* comp = new(Z) RelationalOpInstr(call->token_pos(),
1012 op_kind,
1013 new(Z) Value(left),
1014 new(Z) Value(right),
1015 cid,
1016 call->deopt_id());
1017 ReplaceCall(call, comp);
1018 return true;
1019 }
1020
1021
1022 bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
1023 Token::Kind op_kind) {
1024 intptr_t operands_type = kIllegalCid;
1025 ASSERT(call->HasICData());
1026 const ICData& ic_data = *call->ic_data();
1027 switch (op_kind) {
1028 case Token::kADD:
1029 case Token::kSUB:
1030 case Token::kMUL:
1031 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1032 // Don't generate smi code if the IC data is marked because
1033 // of an overflow.
1034 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)
1035 ? kMintCid
1036 : kSmiCid;
1037 } else if (HasTwoMintOrSmi(ic_data) &&
1038 FlowGraphCompiler::SupportsUnboxedMints()) {
1039 // Don't generate mint code if the IC data is marked because of an
1040 // overflow.
1041 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) return false;
1042 operands_type = kMintCid;
1043 } else if (ShouldSpecializeForDouble(ic_data)) {
1044 operands_type = kDoubleCid;
1045 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) {
1046 operands_type = kFloat32x4Cid;
1047 } else if (HasOnlyTwoOf(ic_data, kInt32x4Cid)) {
1048 ASSERT(op_kind != Token::kMUL); // Int32x4 doesn't have a multiply op.
1049 operands_type = kInt32x4Cid;
1050 } else if (HasOnlyTwoOf(ic_data, kFloat64x2Cid)) {
1051 operands_type = kFloat64x2Cid;
1052 } else {
1053 return false;
1054 }
1055 break;
1056 case Token::kDIV:
1057 if (!FlowGraphCompiler::SupportsHardwareDivision()) return false;
1058 if (ShouldSpecializeForDouble(ic_data) ||
1059 HasOnlyTwoOf(ic_data, kSmiCid)) {
1060 operands_type = kDoubleCid;
1061 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) {
1062 operands_type = kFloat32x4Cid;
1063 } else if (HasOnlyTwoOf(ic_data, kFloat64x2Cid)) {
1064 operands_type = kFloat64x2Cid;
1065 } else {
1066 return false;
1067 }
1068 break;
1069 case Token::kBIT_AND:
1070 case Token::kBIT_OR:
1071 case Token::kBIT_XOR:
1072 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1073 operands_type = kSmiCid;
1074 } else if (HasTwoMintOrSmi(ic_data)) {
1075 operands_type = kMintCid;
1076 } else if (HasOnlyTwoOf(ic_data, kInt32x4Cid)) {
1077 operands_type = kInt32x4Cid;
1078 } else {
1079 return false;
1080 }
1081 break;
1082 case Token::kSHR:
1083 case Token::kSHL:
1084 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1085 // Left shift may overflow from smi into mint or big ints.
1086 // Don't generate smi code if the IC data is marked because
1087 // of an overflow.
1088 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
1089 return false;
1090 }
1091 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)
1092 ? kMintCid
1093 : kSmiCid;
1094 } else if (HasTwoMintOrSmi(ic_data) &&
1095 HasOnlyOneSmi(ICData::Handle(Z,
1096 ic_data.AsUnaryClassChecksForArgNr(1)))) {
1097 // Don't generate mint code if the IC data is marked because of an
1098 // overflow.
1099 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
1100 return false;
1101 }
1102 // Check for smi/mint << smi or smi/mint >> smi.
1103 operands_type = kMintCid;
1104 } else {
1105 return false;
1106 }
1107 break;
1108 case Token::kMOD:
1109 case Token::kTRUNCDIV:
1110 if (!FlowGraphCompiler::SupportsHardwareDivision()) return false;
1111 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1112 if (ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)) {
1113 return false;
1114 }
1115 operands_type = kSmiCid;
1116 } else {
1117 return false;
1118 }
1119 break;
1120 default:
1121 UNREACHABLE();
1122 }
1123
1124 ASSERT(call->ArgumentCount() == 2);
1125 Definition* left = call->ArgumentAt(0);
1126 Definition* right = call->ArgumentAt(1);
1127 if (operands_type == kDoubleCid) {
1128 if (!CanUnboxDouble()) {
1129 return false;
1130 }
1131 // Check that either left or right are not a smi. Result of a
1132 // binary operation with two smis is a smi not a double, except '/' which
1133 // returns a double for two smis.
1134 if (op_kind != Token::kDIV) {
1135 InsertBefore(call,
1136 new(Z) CheckEitherNonSmiInstr(
1137 new(Z) Value(left),
1138 new(Z) Value(right),
1139 call->deopt_id()),
1140 call->env(),
1141 FlowGraph::kEffect);
1142 }
1143
1144 BinaryDoubleOpInstr* double_bin_op =
1145 new(Z) BinaryDoubleOpInstr(op_kind,
1146 new(Z) Value(left),
1147 new(Z) Value(right),
1148 call->deopt_id(), call->token_pos());
1149 ReplaceCall(call, double_bin_op);
1150 } else if (operands_type == kMintCid) {
1151 if (!FlowGraphCompiler::SupportsUnboxedMints()) return false;
1152 if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
1153 ShiftMintOpInstr* shift_op =
1154 new(Z) ShiftMintOpInstr(
1155 op_kind, new(Z) Value(left), new(Z) Value(right),
1156 call->deopt_id());
1157 ReplaceCall(call, shift_op);
1158 } else {
1159 BinaryMintOpInstr* bin_op =
1160 new(Z) BinaryMintOpInstr(
1161 op_kind, new(Z) Value(left), new(Z) Value(right),
1162 call->deopt_id());
1163 ReplaceCall(call, bin_op);
1164 }
1165 } else if (operands_type == kFloat32x4Cid) {
1166 return InlineFloat32x4BinaryOp(call, op_kind);
1167 } else if (operands_type == kInt32x4Cid) {
1168 return InlineInt32x4BinaryOp(call, op_kind);
1169 } else if (operands_type == kFloat64x2Cid) {
1170 return InlineFloat64x2BinaryOp(call, op_kind);
1171 } else if (op_kind == Token::kMOD) {
1172 ASSERT(operands_type == kSmiCid);
1173 if (right->IsConstant()) {
1174 const Object& obj = right->AsConstant()->value();
1175 if (obj.IsSmi() && Utils::IsPowerOfTwo(Smi::Cast(obj).Value())) {
1176 // Insert smi check and attach a copy of the original environment
1177 // because the smi operation can still deoptimize.
1178 InsertBefore(call,
1179 new(Z) CheckSmiInstr(new(Z) Value(left),
1180 call->deopt_id(),
1181 call->token_pos()),
1182 call->env(),
1183 FlowGraph::kEffect);
1184 ConstantInstr* constant =
1185 flow_graph()->GetConstant(Smi::Handle(Z,
1186 Smi::New(Smi::Cast(obj).Value() - 1)));
1187 BinarySmiOpInstr* bin_op =
1188 new(Z) BinarySmiOpInstr(Token::kBIT_AND,
1189 new(Z) Value(left),
1190 new(Z) Value(constant),
1191 call->deopt_id());
1192 ReplaceCall(call, bin_op);
1193 return true;
1194 }
1195 }
1196 // Insert two smi checks and attach a copy of the original
1197 // environment because the smi operation can still deoptimize.
1198 AddCheckSmi(left, call->deopt_id(), call->env(), call);
1199 AddCheckSmi(right, call->deopt_id(), call->env(), call);
1200 BinarySmiOpInstr* bin_op =
1201 new(Z) BinarySmiOpInstr(op_kind,
1202 new(Z) Value(left),
1203 new(Z) Value(right),
1204 call->deopt_id());
1205 ReplaceCall(call, bin_op);
1206 } else {
1207 ASSERT(operands_type == kSmiCid);
1208 // Insert two smi checks and attach a copy of the original
1209 // environment because the smi operation can still deoptimize.
1210 AddCheckSmi(left, call->deopt_id(), call->env(), call);
1211 AddCheckSmi(right, call->deopt_id(), call->env(), call);
1212 if (left->IsConstant() &&
1213 ((op_kind == Token::kADD) || (op_kind == Token::kMUL))) {
1214 // Constant should be on the right side.
1215 Definition* temp = left;
1216 left = right;
1217 right = temp;
1218 }
1219 BinarySmiOpInstr* bin_op =
1220 new(Z) BinarySmiOpInstr(
1221 op_kind,
1222 new(Z) Value(left),
1223 new(Z) Value(right),
1224 call->deopt_id());
1225 ReplaceCall(call, bin_op);
1226 }
1227 return true;
1228 }
1229
1230
1231 bool FlowGraphOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call,
1232 Token::Kind op_kind) {
1233 ASSERT(call->ArgumentCount() == 1);
1234 Definition* input = call->ArgumentAt(0);
1235 Definition* unary_op = NULL;
1236 if (HasOnlyOneSmi(*call->ic_data())) {
1237 InsertBefore(call,
1238 new(Z) CheckSmiInstr(new(Z) Value(input),
1239 call->deopt_id(),
1240 call->token_pos()),
1241 call->env(),
1242 FlowGraph::kEffect);
1243 unary_op = new(Z) UnarySmiOpInstr(
1244 op_kind, new(Z) Value(input), call->deopt_id());
1245 } else if ((op_kind == Token::kBIT_NOT) &&
1246 HasOnlySmiOrMint(*call->ic_data()) &&
1247 FlowGraphCompiler::SupportsUnboxedMints()) {
1248 unary_op = new(Z) UnaryMintOpInstr(
1249 op_kind, new(Z) Value(input), call->deopt_id());
1250 } else if (HasOnlyOneDouble(*call->ic_data()) &&
1251 (op_kind == Token::kNEGATE) &&
1252 CanUnboxDouble()) {
1253 AddReceiverCheck(call);
1254 unary_op = new(Z) UnaryDoubleOpInstr(
1255 Token::kNEGATE, new(Z) Value(input), call->deopt_id());
1256 } else {
1257 return false;
1258 }
1259 ASSERT(unary_op != NULL);
1260 ReplaceCall(call, unary_op);
1261 return true;
1262 }
1263
1264
1265 // Using field class
1266 RawField* FlowGraphOptimizer::GetField(intptr_t class_id,
1267 const String& field_name) {
1268 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id));
1269 Field& field = Field::Handle(Z);
1270 while (!cls.IsNull()) {
1271 field = cls.LookupInstanceField(field_name);
1272 if (!field.IsNull()) {
1273 return field.raw();
1274 }
1275 cls = cls.SuperClass();
1276 }
1277 return Field::null();
1278 }
1279
1280
1281 // Use CHA to determine if the call needs a class check: if the callee's
1282 // receiver is the same as the caller's receiver and there are no overriden
1283 // callee functions, then no class check is needed.
1284 bool FlowGraphOptimizer::InstanceCallNeedsClassCheck(
1285 InstanceCallInstr* call, RawFunction::Kind kind) const {
1286 if (!FLAG_use_cha_deopt && !isolate()->all_classes_finalized()) {
1287 // Even if class or function are private, lazy class finalization
1288 // may later add overriding methods.
1289 return true;
1290 }
1291 Definition* callee_receiver = call->ArgumentAt(0);
1292 ASSERT(callee_receiver != NULL);
1293 const Function& function = flow_graph_->function();
1294 if (function.IsDynamicFunction() &&
1295 callee_receiver->IsParameter() &&
1296 (callee_receiver->AsParameter()->index() == 0)) {
1297 const String& name = (kind == RawFunction::kMethodExtractor)
1298 ? String::Handle(Z, Field::NameFromGetter(call->function_name()))
1299 : call->function_name();
1300 const Class& cls = Class::Handle(Z, function.Owner());
1301 if (!thread()->cha()->HasOverride(cls, name)) {
1302 if (FLAG_trace_cha) {
1303 THR_Print(" **(CHA) Instance call needs no check, "
1304 "no overrides of '%s' '%s'\n",
1305 name.ToCString(), cls.ToCString());
1306 }
1307 thread()->cha()->AddToLeafClasses(cls);
1308 return false;
1309 }
1310 }
1311 return true;
1312 }
1313
1314
1315 bool FlowGraphOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call,
1316 bool allow_check) {
1317 ASSERT(call->HasICData());
1318 const ICData& ic_data = *call->ic_data();
1319 ASSERT(ic_data.HasOneTarget());
1320 GrowableArray<intptr_t> class_ids;
1321 ic_data.GetClassIdsAt(0, &class_ids);
1322 ASSERT(class_ids.length() == 1);
1323 // Inline implicit instance getter.
1324 const String& field_name =
1325 String::Handle(Z, Field::NameFromGetter(call->function_name()));
1326 const Field& field =
1327 Field::ZoneHandle(Z, GetField(class_ids[0], field_name));
1328 ASSERT(!field.IsNull());
1329
1330 if (InstanceCallNeedsClassCheck(call, RawFunction::kImplicitGetter)) {
1331 if (!allow_check) {
1332 return false;
1333 }
1334 AddReceiverCheck(call);
1335 }
1336 LoadFieldInstr* load = new(Z) LoadFieldInstr(
1337 new(Z) Value(call->ArgumentAt(0)),
1338 &field,
1339 AbstractType::ZoneHandle(Z, field.type()),
1340 call->token_pos());
1341 load->set_is_immutable(field.is_final());
1342 if (field.guarded_cid() != kIllegalCid) {
1343 if (!field.is_nullable() || (field.guarded_cid() == kNullCid)) {
1344 load->set_result_cid(field.guarded_cid());
1345 }
1346 flow_graph()->parsed_function().AddToGuardedFields(&field);
1347 }
1348
1349 // Discard the environment from the original instruction because the load
1350 // can't deoptimize.
1351 call->RemoveEnvironment();
1352 ReplaceCall(call, load);
1353
1354 if (load->result_cid() != kDynamicCid) {
1355 // Reset value types if guarded_cid was used.
1356 for (Value::Iterator it(load->input_use_list());
1357 !it.Done();
1358 it.Advance()) {
1359 it.Current()->SetReachingType(NULL);
1360 }
1361 }
1362 return true;
1363 }
1364
1365
1366 bool FlowGraphOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call,
1367 MethodRecognizer::Kind getter) {
1368 if (!ShouldInlineSimd()) {
1369 return false;
1370 }
1371 AddCheckClass(call->ArgumentAt(0),
1372 ICData::ZoneHandle(
1373 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1374 call->deopt_id(),
1375 call->env(),
1376 call);
1377 intptr_t mask = 0;
1378 if ((getter == MethodRecognizer::kFloat32x4Shuffle) ||
1379 (getter == MethodRecognizer::kFloat32x4ShuffleMix)) {
1380 // Extract shuffle mask.
1381 Definition* mask_definition = NULL;
1382 if (getter == MethodRecognizer::kFloat32x4Shuffle) {
1383 ASSERT(call->ArgumentCount() == 2);
1384 mask_definition = call->ArgumentAt(1);
1385 } else {
1386 ASSERT(getter == MethodRecognizer::kFloat32x4ShuffleMix);
1387 ASSERT(call->ArgumentCount() == 3);
1388 mask_definition = call->ArgumentAt(2);
1389 }
1390 if (!mask_definition->IsConstant()) {
1391 return false;
1392 }
1393 ASSERT(mask_definition->IsConstant());
1394 ConstantInstr* constant_instruction = mask_definition->AsConstant();
1395 const Object& constant_mask = constant_instruction->value();
1396 if (!constant_mask.IsSmi()) {
1397 return false;
1398 }
1399 ASSERT(constant_mask.IsSmi());
1400 mask = Smi::Cast(constant_mask).Value();
1401 if ((mask < 0) || (mask > 255)) {
1402 // Not a valid mask.
1403 return false;
1404 }
1405 }
1406 if (getter == MethodRecognizer::kFloat32x4GetSignMask) {
1407 Simd32x4GetSignMaskInstr* instr = new(Z) Simd32x4GetSignMaskInstr(
1408 getter,
1409 new(Z) Value(call->ArgumentAt(0)),
1410 call->deopt_id());
1411 ReplaceCall(call, instr);
1412 return true;
1413 } else if (getter == MethodRecognizer::kFloat32x4ShuffleMix) {
1414 Simd32x4ShuffleMixInstr* instr = new(Z) Simd32x4ShuffleMixInstr(
1415 getter,
1416 new(Z) Value(call->ArgumentAt(0)),
1417 new(Z) Value(call->ArgumentAt(1)),
1418 mask,
1419 call->deopt_id());
1420 ReplaceCall(call, instr);
1421 return true;
1422 } else {
1423 ASSERT((getter == MethodRecognizer::kFloat32x4Shuffle) ||
1424 (getter == MethodRecognizer::kFloat32x4ShuffleX) ||
1425 (getter == MethodRecognizer::kFloat32x4ShuffleY) ||
1426 (getter == MethodRecognizer::kFloat32x4ShuffleZ) ||
1427 (getter == MethodRecognizer::kFloat32x4ShuffleW));
1428 Simd32x4ShuffleInstr* instr = new(Z) Simd32x4ShuffleInstr(
1429 getter,
1430 new(Z) Value(call->ArgumentAt(0)),
1431 mask,
1432 call->deopt_id());
1433 ReplaceCall(call, instr);
1434 return true;
1435 }
1436 UNREACHABLE();
1437 return false;
1438 }
1439
1440
1441 bool FlowGraphOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call,
1442 MethodRecognizer::Kind getter) {
1443 if (!ShouldInlineSimd()) {
1444 return false;
1445 }
1446 AddCheckClass(call->ArgumentAt(0),
1447 ICData::ZoneHandle(
1448 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1449 call->deopt_id(),
1450 call->env(),
1451 call);
1452 if ((getter == MethodRecognizer::kFloat64x2GetX) ||
1453 (getter == MethodRecognizer::kFloat64x2GetY)) {
1454 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr(
1455 getter,
1456 new(Z) Value(call->ArgumentAt(0)),
1457 0,
1458 call->deopt_id());
1459 ReplaceCall(call, instr);
1460 return true;
1461 }
1462 UNREACHABLE();
1463 return false;
1464 }
1465
1466
1467 bool FlowGraphOptimizer::InlineInt32x4Getter(InstanceCallInstr* call,
1468 MethodRecognizer::Kind getter) {
1469 if (!ShouldInlineSimd()) {
1470 return false;
1471 }
1472 AddCheckClass(call->ArgumentAt(0),
1473 ICData::ZoneHandle(
1474 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1475 call->deopt_id(),
1476 call->env(),
1477 call);
1478 intptr_t mask = 0;
1479 if ((getter == MethodRecognizer::kInt32x4Shuffle) ||
1480 (getter == MethodRecognizer::kInt32x4ShuffleMix)) {
1481 // Extract shuffle mask.
1482 Definition* mask_definition = NULL;
1483 if (getter == MethodRecognizer::kInt32x4Shuffle) {
1484 ASSERT(call->ArgumentCount() == 2);
1485 mask_definition = call->ArgumentAt(1);
1486 } else {
1487 ASSERT(getter == MethodRecognizer::kInt32x4ShuffleMix);
1488 ASSERT(call->ArgumentCount() == 3);
1489 mask_definition = call->ArgumentAt(2);
1490 }
1491 if (!mask_definition->IsConstant()) {
1492 return false;
1493 }
1494 ASSERT(mask_definition->IsConstant());
1495 ConstantInstr* constant_instruction = mask_definition->AsConstant();
1496 const Object& constant_mask = constant_instruction->value();
1497 if (!constant_mask.IsSmi()) {
1498 return false;
1499 }
1500 ASSERT(constant_mask.IsSmi());
1501 mask = Smi::Cast(constant_mask).Value();
1502 if ((mask < 0) || (mask > 255)) {
1503 // Not a valid mask.
1504 return false;
1505 }
1506 }
1507 if (getter == MethodRecognizer::kInt32x4GetSignMask) {
1508 Simd32x4GetSignMaskInstr* instr = new(Z) Simd32x4GetSignMaskInstr(
1509 getter,
1510 new(Z) Value(call->ArgumentAt(0)),
1511 call->deopt_id());
1512 ReplaceCall(call, instr);
1513 return true;
1514 } else if (getter == MethodRecognizer::kInt32x4ShuffleMix) {
1515 Simd32x4ShuffleMixInstr* instr = new(Z) Simd32x4ShuffleMixInstr(
1516 getter,
1517 new(Z) Value(call->ArgumentAt(0)),
1518 new(Z) Value(call->ArgumentAt(1)),
1519 mask,
1520 call->deopt_id());
1521 ReplaceCall(call, instr);
1522 return true;
1523 } else if (getter == MethodRecognizer::kInt32x4Shuffle) {
1524 Simd32x4ShuffleInstr* instr = new(Z) Simd32x4ShuffleInstr(
1525 getter,
1526 new(Z) Value(call->ArgumentAt(0)),
1527 mask,
1528 call->deopt_id());
1529 ReplaceCall(call, instr);
1530 return true;
1531 } else {
1532 Int32x4GetFlagInstr* instr = new(Z) Int32x4GetFlagInstr(
1533 getter,
1534 new(Z) Value(call->ArgumentAt(0)),
1535 call->deopt_id());
1536 ReplaceCall(call, instr);
1537 return true;
1538 }
1539 }
1540
1541
1542 bool FlowGraphOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call,
1543 Token::Kind op_kind) {
1544 if (!ShouldInlineSimd()) {
1545 return false;
1546 }
1547 ASSERT(call->ArgumentCount() == 2);
1548 Definition* left = call->ArgumentAt(0);
1549 Definition* right = call->ArgumentAt(1);
1550 // Type check left.
1551 AddCheckClass(left,
1552 ICData::ZoneHandle(
1553 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1554 call->deopt_id(),
1555 call->env(),
1556 call);
1557 // Type check right.
1558 AddCheckClass(right,
1559 ICData::ZoneHandle(
1560 Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)),
1561 call->deopt_id(),
1562 call->env(),
1563 call);
1564 // Replace call.
1565 BinaryFloat32x4OpInstr* float32x4_bin_op =
1566 new(Z) BinaryFloat32x4OpInstr(
1567 op_kind, new(Z) Value(left), new(Z) Value(right),
1568 call->deopt_id());
1569 ReplaceCall(call, float32x4_bin_op);
1570
1571 return true;
1572 }
1573
1574
1575 bool FlowGraphOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call,
1576 Token::Kind op_kind) {
1577 if (!ShouldInlineSimd()) {
1578 return false;
1579 }
1580 ASSERT(call->ArgumentCount() == 2);
1581 Definition* left = call->ArgumentAt(0);
1582 Definition* right = call->ArgumentAt(1);
1583 // Type check left.
1584 AddCheckClass(left,
1585 ICData::ZoneHandle(
1586 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1587 call->deopt_id(),
1588 call->env(),
1589 call);
1590 // Type check right.
1591 AddCheckClass(right,
1592 ICData::ZoneHandle(Z,
1593 call->ic_data()->AsUnaryClassChecksForArgNr(1)),
1594 call->deopt_id(),
1595 call->env(),
1596 call);
1597 // Replace call.
1598 BinaryInt32x4OpInstr* int32x4_bin_op =
1599 new(Z) BinaryInt32x4OpInstr(
1600 op_kind, new(Z) Value(left), new(Z) Value(right),
1601 call->deopt_id());
1602 ReplaceCall(call, int32x4_bin_op);
1603 return true;
1604 }
1605
1606
1607 bool FlowGraphOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call,
1608 Token::Kind op_kind) {
1609 if (!ShouldInlineSimd()) {
1610 return false;
1611 }
1612 ASSERT(call->ArgumentCount() == 2);
1613 Definition* left = call->ArgumentAt(0);
1614 Definition* right = call->ArgumentAt(1);
1615 // Type check left.
1616 AddCheckClass(left,
1617 ICData::ZoneHandle(
1618 call->ic_data()->AsUnaryClassChecksForArgNr(0)),
1619 call->deopt_id(),
1620 call->env(),
1621 call);
1622 // Type check right.
1623 AddCheckClass(right,
1624 ICData::ZoneHandle(
1625 call->ic_data()->AsUnaryClassChecksForArgNr(1)),
1626 call->deopt_id(),
1627 call->env(),
1628 call);
1629 // Replace call.
1630 BinaryFloat64x2OpInstr* float64x2_bin_op =
1631 new(Z) BinaryFloat64x2OpInstr(
1632 op_kind, new(Z) Value(left), new(Z) Value(right),
1633 call->deopt_id());
1634 ReplaceCall(call, float64x2_bin_op);
1635 return true;
1636 }
1637
1638
1639 // Only unique implicit instance getters can be currently handled.
1640 // Returns false if 'allow_check' is false and a check is needed.
1641 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call,
1642 bool allow_check) {
1643 ASSERT(call->HasICData());
1644 const ICData& ic_data = *call->ic_data();
1645 if (ic_data.NumberOfUsedChecks() == 0) {
1646 // No type feedback collected.
1647 return false;
1648 }
1649
1650 if (!ic_data.HasOneTarget()) {
1651 // Polymorphic sites are inlined like normal methods by conventional
1652 // inlining in FlowGraphInliner.
1653 return false;
1654 }
1655
1656 const Function& target = Function::Handle(Z, ic_data.GetTargetAt(0));
1657 if (target.kind() != RawFunction::kImplicitGetter) {
1658 // Non-implicit getters are inlined like normal methods by conventional
1659 // inlining in FlowGraphInliner.
1660 return false;
1661 }
1662 return InlineImplicitInstanceGetter(call, allow_check);
1663 }
1664
1665
1666 bool FlowGraphOptimizer::TryReplaceInstanceCallWithInline(
1667 InstanceCallInstr* call) {
1668 Function& target = Function::Handle(Z);
1669 GrowableArray<intptr_t> class_ids;
1670 call->ic_data()->GetCheckAt(0, &class_ids, &target);
1671 const intptr_t receiver_cid = class_ids[0];
1672
1673 TargetEntryInstr* entry;
1674 Definition* last;
1675 if (!FlowGraphInliner::TryInlineRecognizedMethod(flow_graph_,
1676 receiver_cid,
1677 target,
1678 call,
1679 call->ArgumentAt(0),
1680 call->token_pos(),
1681 *call->ic_data(),
1682 &entry, &last)) {
1683 return false;
1684 }
1685
1686 // Insert receiver class check.
1687 AddReceiverCheck(call);
1688 // Remove the original push arguments.
1689 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
1690 PushArgumentInstr* push = call->PushArgumentAt(i);
1691 push->ReplaceUsesWith(push->value()->definition());
1692 push->RemoveFromGraph();
1693 }
1694 // Replace all uses of this definition with the result.
1695 call->ReplaceUsesWith(last);
1696 // Finally insert the sequence other definition in place of this one in the
1697 // graph.
1698 call->previous()->LinkTo(entry->next());
1699 entry->UnuseAllInputs(); // Entry block is not in the graph.
1700 last->LinkTo(call);
1701 // Remove through the iterator.
1702 ASSERT(current_iterator()->Current() == call);
1703 current_iterator()->RemoveCurrentFromGraph();
1704 call->set_previous(NULL);
1705 call->set_next(NULL);
1706 return true;
1707 }
1708
1709
1710 void FlowGraphOptimizer::ReplaceWithMathCFunction(
1711 InstanceCallInstr* call,
1712 MethodRecognizer::Kind recognized_kind) {
1713 AddReceiverCheck(call);
1714 ZoneGrowableArray<Value*>* args =
1715 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount());
1716 for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
1717 args->Add(new(Z) Value(call->ArgumentAt(i)));
1718 }
1719 InvokeMathCFunctionInstr* invoke =
1720 new(Z) InvokeMathCFunctionInstr(args,
1721 call->deopt_id(),
1722 recognized_kind,
1723 call->token_pos());
1724 ReplaceCall(call, invoke);
1725 }
1726
1727
1728 static bool IsSupportedByteArrayViewCid(intptr_t cid) {
1729 switch (cid) {
1730 case kTypedDataInt8ArrayCid:
1731 case kTypedDataUint8ArrayCid:
1732 case kExternalTypedDataUint8ArrayCid:
1733 case kTypedDataUint8ClampedArrayCid:
1734 case kExternalTypedDataUint8ClampedArrayCid:
1735 case kTypedDataInt16ArrayCid:
1736 case kTypedDataUint16ArrayCid:
1737 case kTypedDataInt32ArrayCid:
1738 case kTypedDataUint32ArrayCid:
1739 case kTypedDataFloat32ArrayCid:
1740 case kTypedDataFloat64ArrayCid:
1741 case kTypedDataFloat32x4ArrayCid:
1742 case kTypedDataInt32x4ArrayCid:
1743 return true;
1744 default:
1745 return false;
1746 }
1747 }
1748
1749
1750 // Inline only simple, frequently called core library methods.
1751 bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) {
1752 ASSERT(call->HasICData());
1753 const ICData& ic_data = *call->ic_data();
1754 if ((ic_data.NumberOfUsedChecks() == 0) || !ic_data.HasOneTarget()) {
1755 // No type feedback collected or multiple targets found.
1756 return false;
1757 }
1758
1759 Function& target = Function::Handle(Z);
1760 GrowableArray<intptr_t> class_ids;
1761 ic_data.GetCheckAt(0, &class_ids, &target);
1762 MethodRecognizer::Kind recognized_kind =
1763 MethodRecognizer::RecognizeKind(target);
1764
1765 if ((recognized_kind == MethodRecognizer::kGrowableArraySetData) &&
1766 (ic_data.NumberOfChecks() == 1) &&
1767 (class_ids[0] == kGrowableObjectArrayCid)) {
1768 // This is an internal method, no need to check argument types.
1769 Definition* array = call->ArgumentAt(0);
1770 Definition* value = call->ArgumentAt(1);
1771 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr(
1772 GrowableObjectArray::data_offset(),
1773 new(Z) Value(array),
1774 new(Z) Value(value),
1775 kEmitStoreBarrier,
1776 call->token_pos());
1777 ReplaceCall(call, store);
1778 return true;
1779 }
1780
1781 if ((recognized_kind == MethodRecognizer::kGrowableArraySetLength) &&
1782 (ic_data.NumberOfChecks() == 1) &&
1783 (class_ids[0] == kGrowableObjectArrayCid)) {
1784 // This is an internal method, no need to check argument types nor
1785 // range.
1786 Definition* array = call->ArgumentAt(0);
1787 Definition* value = call->ArgumentAt(1);
1788 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr(
1789 GrowableObjectArray::length_offset(),
1790 new(Z) Value(array),
1791 new(Z) Value(value),
1792 kNoStoreBarrier,
1793 call->token_pos());
1794 ReplaceCall(call, store);
1795 return true;
1796 }
1797
1798 if (((recognized_kind == MethodRecognizer::kStringBaseCodeUnitAt) ||
1799 (recognized_kind == MethodRecognizer::kStringBaseCharAt)) &&
1800 (ic_data.NumberOfChecks() == 1) &&
1801 ((class_ids[0] == kOneByteStringCid) ||
1802 (class_ids[0] == kTwoByteStringCid))) {
1803 return TryReplaceInstanceCallWithInline(call);
1804 }
1805
1806 if ((class_ids[0] == kOneByteStringCid) && (ic_data.NumberOfChecks() == 1)) {
1807 if (recognized_kind == MethodRecognizer::kOneByteStringSetAt) {
1808 // This is an internal method, no need to check argument types nor
1809 // range.
1810 Definition* str = call->ArgumentAt(0);
1811 Definition* index = call->ArgumentAt(1);
1812 Definition* value = call->ArgumentAt(2);
1813 StoreIndexedInstr* store_op = new(Z) StoreIndexedInstr(
1814 new(Z) Value(str),
1815 new(Z) Value(index),
1816 new(Z) Value(value),
1817 kNoStoreBarrier,
1818 1, // Index scale
1819 kOneByteStringCid,
1820 call->deopt_id(),
1821 call->token_pos());
1822 ReplaceCall(call, store_op);
1823 return true;
1824 }
1825 return false;
1826 }
1827
1828 if (CanUnboxDouble() &&
1829 (recognized_kind == MethodRecognizer::kIntegerToDouble) &&
1830 (ic_data.NumberOfChecks() == 1)) {
1831 if (class_ids[0] == kSmiCid) {
1832 AddReceiverCheck(call);
1833 ReplaceCall(call,
1834 new(Z) SmiToDoubleInstr(
1835 new(Z) Value(call->ArgumentAt(0)),
1836 call->token_pos()));
1837 return true;
1838 } else if ((class_ids[0] == kMintCid) && CanConvertUnboxedMintToDouble()) {
1839 AddReceiverCheck(call);
1840 ReplaceCall(call,
1841 new(Z) MintToDoubleInstr(new(Z) Value(call->ArgumentAt(0)),
1842 call->deopt_id()));
1843 return true;
1844 }
1845 }
1846
1847 if (class_ids[0] == kDoubleCid) {
1848 if (!CanUnboxDouble()) {
1849 return false;
1850 }
1851 switch (recognized_kind) {
1852 case MethodRecognizer::kDoubleToInteger: {
1853 AddReceiverCheck(call);
1854 ASSERT(call->HasICData());
1855 const ICData& ic_data = *call->ic_data();
1856 Definition* input = call->ArgumentAt(0);
1857 Definition* d2i_instr = NULL;
1858 if (ic_data.HasDeoptReason(ICData::kDeoptDoubleToSmi)) {
1859 // Do not repeatedly deoptimize because result didn't fit into Smi.
1860 d2i_instr = new(Z) DoubleToIntegerInstr(
1861 new(Z) Value(input), call);
1862 } else {
1863 // Optimistically assume result fits into Smi.
1864 d2i_instr = new(Z) DoubleToSmiInstr(
1865 new(Z) Value(input), call->deopt_id());
1866 }
1867 ReplaceCall(call, d2i_instr);
1868 return true;
1869 }
1870 case MethodRecognizer::kDoubleMod:
1871 case MethodRecognizer::kDoubleRound:
1872 ReplaceWithMathCFunction(call, recognized_kind);
1873 return true;
1874 case MethodRecognizer::kDoubleTruncate:
1875 case MethodRecognizer::kDoubleFloor:
1876 case MethodRecognizer::kDoubleCeil:
1877 if (!TargetCPUFeatures::double_truncate_round_supported()) {
1878 ReplaceWithMathCFunction(call, recognized_kind);
1879 } else {
1880 AddReceiverCheck(call);
1881 DoubleToDoubleInstr* d2d_instr =
1882 new(Z) DoubleToDoubleInstr(new(Z) Value(call->ArgumentAt(0)),
1883 recognized_kind, call->deopt_id());
1884 ReplaceCall(call, d2d_instr);
1885 }
1886 return true;
1887 case MethodRecognizer::kDoubleAdd:
1888 case MethodRecognizer::kDoubleSub:
1889 case MethodRecognizer::kDoubleMul:
1890 case MethodRecognizer::kDoubleDiv:
1891 return TryReplaceInstanceCallWithInline(call);
1892 default:
1893 // Unsupported method.
1894 return false;
1895 }
1896 }
1897
1898 if (IsSupportedByteArrayViewCid(class_ids[0]) &&
1899 (ic_data.NumberOfChecks() == 1)) {
1900 return TryReplaceInstanceCallWithInline(call);
1901 }
1902
1903 if ((class_ids[0] == kFloat32x4Cid) && (ic_data.NumberOfChecks() == 1)) {
1904 return TryInlineFloat32x4Method(call, recognized_kind);
1905 }
1906
1907 if ((class_ids[0] == kInt32x4Cid) && (ic_data.NumberOfChecks() == 1)) {
1908 return TryInlineInt32x4Method(call, recognized_kind);
1909 }
1910
1911 if ((class_ids[0] == kFloat64x2Cid) && (ic_data.NumberOfChecks() == 1)) {
1912 return TryInlineFloat64x2Method(call, recognized_kind);
1913 }
1914
1915 if (recognized_kind == MethodRecognizer::kIntegerLeftShiftWithMask32) {
1916 ASSERT(call->ArgumentCount() == 3);
1917 ASSERT(ic_data.NumArgsTested() == 2);
1918 Definition* value = call->ArgumentAt(0);
1919 Definition* count = call->ArgumentAt(1);
1920 Definition* int32_mask = call->ArgumentAt(2);
1921 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1922 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
1923 return false;
1924 }
1925 // We cannot overflow. The input value must be a Smi
1926 AddCheckSmi(value, call->deopt_id(), call->env(), call);
1927 AddCheckSmi(count, call->deopt_id(), call->env(), call);
1928 ASSERT(int32_mask->IsConstant());
1929 const Integer& mask_literal = Integer::Cast(
1930 int32_mask->AsConstant()->value());
1931 const int64_t mask_value = mask_literal.AsInt64Value();
1932 ASSERT(mask_value >= 0);
1933 if (mask_value > Smi::kMaxValue) {
1934 // The result will not be Smi.
1935 return false;
1936 }
1937 BinarySmiOpInstr* left_shift =
1938 new(Z) BinarySmiOpInstr(Token::kSHL,
1939 new(Z) Value(value),
1940 new(Z) Value(count),
1941 call->deopt_id());
1942 left_shift->mark_truncating();
1943 if ((kBitsPerWord == 32) && (mask_value == 0xffffffffLL)) {
1944 // No BIT_AND operation needed.
1945 ReplaceCall(call, left_shift);
1946 } else {
1947 InsertBefore(call, left_shift, call->env(), FlowGraph::kValue);
1948 BinarySmiOpInstr* bit_and =
1949 new(Z) BinarySmiOpInstr(Token::kBIT_AND,
1950 new(Z) Value(left_shift),
1951 new(Z) Value(int32_mask),
1952 call->deopt_id());
1953 ReplaceCall(call, bit_and);
1954 }
1955 return true;
1956 }
1957
1958 if (HasTwoMintOrSmi(ic_data) &&
1959 HasOnlyOneSmi(ICData::Handle(Z,
1960 ic_data.AsUnaryClassChecksForArgNr(1)))) {
1961 if (!FlowGraphCompiler::SupportsUnboxedMints() ||
1962 ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
1963 return false;
1964 }
1965 ShiftMintOpInstr* left_shift =
1966 new(Z) ShiftMintOpInstr(Token::kSHL,
1967 new(Z) Value(value),
1968 new(Z) Value(count),
1969 call->deopt_id());
1970 InsertBefore(call, left_shift, call->env(), FlowGraph::kValue);
1971 BinaryMintOpInstr* bit_and =
1972 new(Z) BinaryMintOpInstr(Token::kBIT_AND,
1973 new(Z) Value(left_shift),
1974 new(Z) Value(int32_mask),
1975 call->deopt_id());
1976 ReplaceCall(call, bit_and);
1977 return true;
1978 }
1979 }
1980 return false;
1981 }
1982
1983
1984 bool FlowGraphOptimizer::TryInlineFloat32x4Constructor(
1985 StaticCallInstr* call,
1986 MethodRecognizer::Kind recognized_kind) {
1987 if (!ShouldInlineSimd()) {
1988 return false;
1989 }
1990 if (recognized_kind == MethodRecognizer::kFloat32x4Zero) {
1991 Float32x4ZeroInstr* zero = new(Z) Float32x4ZeroInstr();
1992 ReplaceCall(call, zero);
1993 return true;
1994 } else if (recognized_kind == MethodRecognizer::kFloat32x4Splat) {
1995 Float32x4SplatInstr* splat =
1996 new(Z) Float32x4SplatInstr(
1997 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
1998 ReplaceCall(call, splat);
1999 return true;
2000 } else if (recognized_kind == MethodRecognizer::kFloat32x4Constructor) {
2001 Float32x4ConstructorInstr* con =
2002 new(Z) Float32x4ConstructorInstr(
2003 new(Z) Value(call->ArgumentAt(1)),
2004 new(Z) Value(call->ArgumentAt(2)),
2005 new(Z) Value(call->ArgumentAt(3)),
2006 new(Z) Value(call->ArgumentAt(4)),
2007 call->deopt_id());
2008 ReplaceCall(call, con);
2009 return true;
2010 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromInt32x4Bits) {
2011 Int32x4ToFloat32x4Instr* cast =
2012 new(Z) Int32x4ToFloat32x4Instr(
2013 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
2014 ReplaceCall(call, cast);
2015 return true;
2016 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromFloat64x2) {
2017 Float64x2ToFloat32x4Instr* cast =
2018 new(Z) Float64x2ToFloat32x4Instr(
2019 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
2020 ReplaceCall(call, cast);
2021 return true;
2022 }
2023 return false;
2024 }
2025
2026
2027 bool FlowGraphOptimizer::TryInlineFloat64x2Constructor(
2028 StaticCallInstr* call,
2029 MethodRecognizer::Kind recognized_kind) {
2030 if (!ShouldInlineSimd()) {
2031 return false;
2032 }
2033 if (recognized_kind == MethodRecognizer::kFloat64x2Zero) {
2034 Float64x2ZeroInstr* zero = new(Z) Float64x2ZeroInstr();
2035 ReplaceCall(call, zero);
2036 return true;
2037 } else if (recognized_kind == MethodRecognizer::kFloat64x2Splat) {
2038 Float64x2SplatInstr* splat =
2039 new(Z) Float64x2SplatInstr(
2040 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
2041 ReplaceCall(call, splat);
2042 return true;
2043 } else if (recognized_kind == MethodRecognizer::kFloat64x2Constructor) {
2044 Float64x2ConstructorInstr* con =
2045 new(Z) Float64x2ConstructorInstr(
2046 new(Z) Value(call->ArgumentAt(1)),
2047 new(Z) Value(call->ArgumentAt(2)),
2048 call->deopt_id());
2049 ReplaceCall(call, con);
2050 return true;
2051 } else if (recognized_kind == MethodRecognizer::kFloat64x2FromFloat32x4) {
2052 Float32x4ToFloat64x2Instr* cast =
2053 new(Z) Float32x4ToFloat64x2Instr(
2054 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
2055 ReplaceCall(call, cast);
2056 return true;
2057 }
2058 return false;
2059 }
2060
2061
2062 bool FlowGraphOptimizer::TryInlineInt32x4Constructor(
2063 StaticCallInstr* call,
2064 MethodRecognizer::Kind recognized_kind) {
2065 if (!ShouldInlineSimd()) {
2066 return false;
2067 }
2068 if (recognized_kind == MethodRecognizer::kInt32x4BoolConstructor) {
2069 Int32x4BoolConstructorInstr* con =
2070 new(Z) Int32x4BoolConstructorInstr(
2071 new(Z) Value(call->ArgumentAt(1)),
2072 new(Z) Value(call->ArgumentAt(2)),
2073 new(Z) Value(call->ArgumentAt(3)),
2074 new(Z) Value(call->ArgumentAt(4)),
2075 call->deopt_id());
2076 ReplaceCall(call, con);
2077 return true;
2078 } else if (recognized_kind == MethodRecognizer::kInt32x4FromFloat32x4Bits) {
2079 Float32x4ToInt32x4Instr* cast =
2080 new(Z) Float32x4ToInt32x4Instr(
2081 new(Z) Value(call->ArgumentAt(1)), call->deopt_id());
2082 ReplaceCall(call, cast);
2083 return true;
2084 } else if (recognized_kind == MethodRecognizer::kInt32x4Constructor) {
2085 Int32x4ConstructorInstr* con =
2086 new(Z) Int32x4ConstructorInstr(
2087 new(Z) Value(call->ArgumentAt(1)),
2088 new(Z) Value(call->ArgumentAt(2)),
2089 new(Z) Value(call->ArgumentAt(3)),
2090 new(Z) Value(call->ArgumentAt(4)),
2091 call->deopt_id());
2092 ReplaceCall(call, con);
2093 return true;
2094 }
2095 return false;
2096 }
2097
2098
2099 bool FlowGraphOptimizer::TryInlineFloat32x4Method(
2100 InstanceCallInstr* call,
2101 MethodRecognizer::Kind recognized_kind) {
2102 if (!ShouldInlineSimd()) {
2103 return false;
2104 }
2105 ASSERT(call->HasICData());
2106 switch (recognized_kind) {
2107 case MethodRecognizer::kFloat32x4ShuffleX:
2108 case MethodRecognizer::kFloat32x4ShuffleY:
2109 case MethodRecognizer::kFloat32x4ShuffleZ:
2110 case MethodRecognizer::kFloat32x4ShuffleW:
2111 case MethodRecognizer::kFloat32x4GetSignMask:
2112 ASSERT(call->ic_data()->HasReceiverClassId(kFloat32x4Cid));
2113 ASSERT(call->ic_data()->HasOneTarget());
2114 return InlineFloat32x4Getter(call, recognized_kind);
2115
2116 case MethodRecognizer::kFloat32x4Equal:
2117 case MethodRecognizer::kFloat32x4GreaterThan:
2118 case MethodRecognizer::kFloat32x4GreaterThanOrEqual:
2119 case MethodRecognizer::kFloat32x4LessThan:
2120 case MethodRecognizer::kFloat32x4LessThanOrEqual:
2121 case MethodRecognizer::kFloat32x4NotEqual: {
2122 Definition* left = call->ArgumentAt(0);
2123 Definition* right = call->ArgumentAt(1);
2124 // Type check left.
2125 AddCheckClass(left,
2126 ICData::ZoneHandle(
2127 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2128 call->deopt_id(),
2129 call->env(),
2130 call);
2131 // Replace call.
2132 Float32x4ComparisonInstr* cmp =
2133 new(Z) Float32x4ComparisonInstr(recognized_kind,
2134 new(Z) Value(left),
2135 new(Z) Value(right),
2136 call->deopt_id());
2137 ReplaceCall(call, cmp);
2138 return true;
2139 }
2140 case MethodRecognizer::kFloat32x4Min:
2141 case MethodRecognizer::kFloat32x4Max: {
2142 Definition* left = call->ArgumentAt(0);
2143 Definition* right = call->ArgumentAt(1);
2144 // Type check left.
2145 AddCheckClass(left,
2146 ICData::ZoneHandle(
2147 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2148 call->deopt_id(),
2149 call->env(),
2150 call);
2151 Float32x4MinMaxInstr* minmax =
2152 new(Z) Float32x4MinMaxInstr(
2153 recognized_kind,
2154 new(Z) Value(left),
2155 new(Z) Value(right),
2156 call->deopt_id());
2157 ReplaceCall(call, minmax);
2158 return true;
2159 }
2160 case MethodRecognizer::kFloat32x4Scale: {
2161 Definition* left = call->ArgumentAt(0);
2162 Definition* right = call->ArgumentAt(1);
2163 // Type check left.
2164 AddCheckClass(left,
2165 ICData::ZoneHandle(
2166 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2167 call->deopt_id(),
2168 call->env(),
2169 call);
2170 // Left and right values are swapped when handed to the instruction,
2171 // this is done so that the double value is loaded into the output
2172 // register and can be destroyed.
2173 Float32x4ScaleInstr* scale =
2174 new(Z) Float32x4ScaleInstr(recognized_kind,
2175 new(Z) Value(right),
2176 new(Z) Value(left),
2177 call->deopt_id());
2178 ReplaceCall(call, scale);
2179 return true;
2180 }
2181 case MethodRecognizer::kFloat32x4Sqrt:
2182 case MethodRecognizer::kFloat32x4ReciprocalSqrt:
2183 case MethodRecognizer::kFloat32x4Reciprocal: {
2184 Definition* left = call->ArgumentAt(0);
2185 AddCheckClass(left,
2186 ICData::ZoneHandle(
2187 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2188 call->deopt_id(),
2189 call->env(),
2190 call);
2191 Float32x4SqrtInstr* sqrt =
2192 new(Z) Float32x4SqrtInstr(recognized_kind,
2193 new(Z) Value(left),
2194 call->deopt_id());
2195 ReplaceCall(call, sqrt);
2196 return true;
2197 }
2198 case MethodRecognizer::kFloat32x4WithX:
2199 case MethodRecognizer::kFloat32x4WithY:
2200 case MethodRecognizer::kFloat32x4WithZ:
2201 case MethodRecognizer::kFloat32x4WithW: {
2202 Definition* left = call->ArgumentAt(0);
2203 Definition* right = call->ArgumentAt(1);
2204 // Type check left.
2205 AddCheckClass(left,
2206 ICData::ZoneHandle(
2207 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2208 call->deopt_id(),
2209 call->env(),
2210 call);
2211 Float32x4WithInstr* with = new(Z) Float32x4WithInstr(recognized_kind,
2212 new(Z) Value(left),
2213 new(Z) Value(right),
2214 call->deopt_id());
2215 ReplaceCall(call, with);
2216 return true;
2217 }
2218 case MethodRecognizer::kFloat32x4Absolute:
2219 case MethodRecognizer::kFloat32x4Negate: {
2220 Definition* left = call->ArgumentAt(0);
2221 // Type check left.
2222 AddCheckClass(left,
2223 ICData::ZoneHandle(
2224 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2225 call->deopt_id(),
2226 call->env(),
2227 call);
2228 Float32x4ZeroArgInstr* zeroArg =
2229 new(Z) Float32x4ZeroArgInstr(
2230 recognized_kind, new(Z) Value(left), call->deopt_id());
2231 ReplaceCall(call, zeroArg);
2232 return true;
2233 }
2234 case MethodRecognizer::kFloat32x4Clamp: {
2235 Definition* left = call->ArgumentAt(0);
2236 Definition* lower = call->ArgumentAt(1);
2237 Definition* upper = call->ArgumentAt(2);
2238 // Type check left.
2239 AddCheckClass(left,
2240 ICData::ZoneHandle(
2241 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2242 call->deopt_id(),
2243 call->env(),
2244 call);
2245 Float32x4ClampInstr* clamp = new(Z) Float32x4ClampInstr(
2246 new(Z) Value(left),
2247 new(Z) Value(lower),
2248 new(Z) Value(upper),
2249 call->deopt_id());
2250 ReplaceCall(call, clamp);
2251 return true;
2252 }
2253 case MethodRecognizer::kFloat32x4ShuffleMix:
2254 case MethodRecognizer::kFloat32x4Shuffle: {
2255 return InlineFloat32x4Getter(call, recognized_kind);
2256 }
2257 default:
2258 return false;
2259 }
2260 }
2261
2262
2263 bool FlowGraphOptimizer::TryInlineFloat64x2Method(
2264 InstanceCallInstr* call,
2265 MethodRecognizer::Kind recognized_kind) {
2266 if (!ShouldInlineSimd()) {
2267 return false;
2268 }
2269 ASSERT(call->HasICData());
2270 switch (recognized_kind) {
2271 case MethodRecognizer::kFloat64x2GetX:
2272 case MethodRecognizer::kFloat64x2GetY:
2273 ASSERT(call->ic_data()->HasReceiverClassId(kFloat64x2Cid));
2274 ASSERT(call->ic_data()->HasOneTarget());
2275 return InlineFloat64x2Getter(call, recognized_kind);
2276 case MethodRecognizer::kFloat64x2Negate:
2277 case MethodRecognizer::kFloat64x2Abs:
2278 case MethodRecognizer::kFloat64x2Sqrt:
2279 case MethodRecognizer::kFloat64x2GetSignMask: {
2280 Definition* left = call->ArgumentAt(0);
2281 // Type check left.
2282 AddCheckClass(left,
2283 ICData::ZoneHandle(
2284 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2285 call->deopt_id(),
2286 call->env(),
2287 call);
2288 Float64x2ZeroArgInstr* zeroArg =
2289 new(Z) Float64x2ZeroArgInstr(
2290 recognized_kind, new(Z) Value(left), call->deopt_id());
2291 ReplaceCall(call, zeroArg);
2292 return true;
2293 }
2294 case MethodRecognizer::kFloat64x2Scale:
2295 case MethodRecognizer::kFloat64x2WithX:
2296 case MethodRecognizer::kFloat64x2WithY:
2297 case MethodRecognizer::kFloat64x2Min:
2298 case MethodRecognizer::kFloat64x2Max: {
2299 Definition* left = call->ArgumentAt(0);
2300 Definition* right = call->ArgumentAt(1);
2301 // Type check left.
2302 AddCheckClass(left,
2303 ICData::ZoneHandle(
2304 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2305 call->deopt_id(),
2306 call->env(),
2307 call);
2308 Float64x2OneArgInstr* zeroArg =
2309 new(Z) Float64x2OneArgInstr(recognized_kind,
2310 new(Z) Value(left),
2311 new(Z) Value(right),
2312 call->deopt_id());
2313 ReplaceCall(call, zeroArg);
2314 return true;
2315 }
2316 default:
2317 return false;
2318 }
2319 }
2320
2321
2322 bool FlowGraphOptimizer::TryInlineInt32x4Method(
2323 InstanceCallInstr* call,
2324 MethodRecognizer::Kind recognized_kind) {
2325 if (!ShouldInlineSimd()) {
2326 return false;
2327 }
2328 ASSERT(call->HasICData());
2329 switch (recognized_kind) {
2330 case MethodRecognizer::kInt32x4ShuffleMix:
2331 case MethodRecognizer::kInt32x4Shuffle:
2332 case MethodRecognizer::kInt32x4GetFlagX:
2333 case MethodRecognizer::kInt32x4GetFlagY:
2334 case MethodRecognizer::kInt32x4GetFlagZ:
2335 case MethodRecognizer::kInt32x4GetFlagW:
2336 case MethodRecognizer::kInt32x4GetSignMask:
2337 ASSERT(call->ic_data()->HasReceiverClassId(kInt32x4Cid));
2338 ASSERT(call->ic_data()->HasOneTarget());
2339 return InlineInt32x4Getter(call, recognized_kind);
2340
2341 case MethodRecognizer::kInt32x4Select: {
2342 Definition* mask = call->ArgumentAt(0);
2343 Definition* trueValue = call->ArgumentAt(1);
2344 Definition* falseValue = call->ArgumentAt(2);
2345 // Type check left.
2346 AddCheckClass(mask,
2347 ICData::ZoneHandle(
2348 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2349 call->deopt_id(),
2350 call->env(),
2351 call);
2352 Int32x4SelectInstr* select = new(Z) Int32x4SelectInstr(
2353 new(Z) Value(mask),
2354 new(Z) Value(trueValue),
2355 new(Z) Value(falseValue),
2356 call->deopt_id());
2357 ReplaceCall(call, select);
2358 return true;
2359 }
2360 case MethodRecognizer::kInt32x4WithFlagX:
2361 case MethodRecognizer::kInt32x4WithFlagY:
2362 case MethodRecognizer::kInt32x4WithFlagZ:
2363 case MethodRecognizer::kInt32x4WithFlagW: {
2364 Definition* left = call->ArgumentAt(0);
2365 Definition* flag = call->ArgumentAt(1);
2366 // Type check left.
2367 AddCheckClass(left,
2368 ICData::ZoneHandle(
2369 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2370 call->deopt_id(),
2371 call->env(),
2372 call);
2373 Int32x4SetFlagInstr* setFlag = new(Z) Int32x4SetFlagInstr(
2374 recognized_kind,
2375 new(Z) Value(left),
2376 new(Z) Value(flag),
2377 call->deopt_id());
2378 ReplaceCall(call, setFlag);
2379 return true;
2380 }
2381 default:
2382 return false;
2383 }
2384 }
2385
2386
2387 // If type tests specified by 'ic_data' do not depend on type arguments,
2388 // return mapping cid->result in 'results' (i : cid; i + 1: result).
2389 // If all tests yield the same result, return it otherwise return Bool::null.
2390 // If no mapping is possible, 'results' is empty.
2391 // An instance-of test returning all same results can be converted to a class
2392 // check.
2393 RawBool* FlowGraphOptimizer::InstanceOfAsBool(
2394 const ICData& ic_data,
2395 const AbstractType& type,
2396 ZoneGrowableArray<intptr_t>* results) const {
2397 ASSERT(results->is_empty());
2398 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only.
2399 if (type.IsFunctionType() || type.IsDartFunctionType() ||
2400 !type.IsInstantiated() || type.IsMalformedOrMalbounded()) {
2401 return Bool::null();
2402 }
2403 const Class& type_class = Class::Handle(Z, type.type_class());
2404 const intptr_t num_type_args = type_class.NumTypeArguments();
2405 if (num_type_args > 0) {
2406 // Only raw types can be directly compared, thus disregarding type
2407 // arguments.
2408 const intptr_t num_type_params = type_class.NumTypeParameters();
2409 const intptr_t from_index = num_type_args - num_type_params;
2410 const TypeArguments& type_arguments =
2411 TypeArguments::Handle(Z, type.arguments());
2412 const bool is_raw_type = type_arguments.IsNull() ||
2413 type_arguments.IsRaw(from_index, num_type_params);
2414 if (!is_raw_type) {
2415 // Unknown result.
2416 return Bool::null();
2417 }
2418 }
2419
2420 const ClassTable& class_table = *isolate()->class_table();
2421 Bool& prev = Bool::Handle(Z);
2422 Class& cls = Class::Handle(Z);
2423
2424 bool results_differ = false;
2425 for (int i = 0; i < ic_data.NumberOfChecks(); i++) {
2426 cls = class_table.At(ic_data.GetReceiverClassIdAt(i));
2427 if (cls.NumTypeArguments() > 0) {
2428 return Bool::null();
2429 }
2430 const bool is_subtype = cls.IsSubtypeOf(
2431 TypeArguments::Handle(Z),
2432 type_class,
2433 TypeArguments::Handle(Z),
2434 NULL,
2435 NULL,
2436 Heap::kOld);
2437 results->Add(cls.id());
2438 results->Add(is_subtype);
2439 if (prev.IsNull()) {
2440 prev = Bool::Get(is_subtype).raw();
2441 } else {
2442 if (is_subtype != prev.value()) {
2443 results_differ = true;
2444 }
2445 }
2446 }
2447 return results_differ ? Bool::null() : prev.raw();
2448 }
2449
2450
2451 // Returns true if checking against this type is a direct class id comparison.
2452 bool FlowGraphOptimizer::TypeCheckAsClassEquality(const AbstractType& type) {
2453 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
2454 // Requires CHA.
2455 if (!type.IsInstantiated()) return false;
2456 // Function types have different type checking rules.
2457 if (type.IsFunctionType()) return false;
2458 const Class& type_class = Class::Handle(type.type_class());
2459 // Could be an interface check?
2460 if (CHA::IsImplemented(type_class)) return false;
2461 // Check if there are subclasses.
2462 if (CHA::HasSubclasses(type_class)) {
2463 return false;
2464 }
2465
2466 // Private classes cannot be subclassed by later loaded libs.
2467 if (!type_class.IsPrivate()) {
2468 if (FLAG_use_cha_deopt || isolate()->all_classes_finalized()) {
2469 if (FLAG_trace_cha) {
2470 THR_Print(" **(CHA) Typecheck as class equality since no "
2471 "subclasses: %s\n",
2472 type_class.ToCString());
2473 }
2474 if (FLAG_use_cha_deopt) {
2475 thread()->cha()->AddToLeafClasses(type_class);
2476 }
2477 } else {
2478 return false;
2479 }
2480 }
2481 const intptr_t num_type_args = type_class.NumTypeArguments();
2482 if (num_type_args > 0) {
2483 // Only raw types can be directly compared, thus disregarding type
2484 // arguments.
2485 const intptr_t num_type_params = type_class.NumTypeParameters();
2486 const intptr_t from_index = num_type_args - num_type_params;
2487 const TypeArguments& type_arguments =
2488 TypeArguments::Handle(type.arguments());
2489 const bool is_raw_type = type_arguments.IsNull() ||
2490 type_arguments.IsRaw(from_index, num_type_params);
2491 return is_raw_type;
2492 }
2493 return true;
2494 }
2495
2496
2497 static bool CidTestResultsContains(const ZoneGrowableArray<intptr_t>& results,
2498 intptr_t test_cid) {
2499 for (intptr_t i = 0; i < results.length(); i += 2) {
2500 if (results[i] == test_cid) return true;
2501 }
2502 return false;
2503 }
2504
2505
2506 static void TryAddTest(ZoneGrowableArray<intptr_t>* results,
2507 intptr_t test_cid,
2508 bool result) {
2509 if (!CidTestResultsContains(*results, test_cid)) {
2510 results->Add(test_cid);
2511 results->Add(result);
2512 }
2513 }
2514
2515
2516 // Tries to add cid tests to 'results' so that no deoptimization is
2517 // necessary.
2518 // TODO(srdjan): Do also for other than 'int' type.
2519 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results,
2520 const AbstractType& type) {
2521 ASSERT(results->length() >= 2); // At least on eentry.
2522 const ClassTable& class_table = *Isolate::Current()->class_table();
2523 if ((*results)[0] != kSmiCid) {
2524 const Class& cls = Class::Handle(class_table.At(kSmiCid));
2525 const Class& type_class = Class::Handle(type.type_class());
2526 const bool smi_is_subtype = cls.IsSubtypeOf(TypeArguments::Handle(),
2527 type_class,
2528 TypeArguments::Handle(),
2529 NULL,
2530 NULL,
2531 Heap::kOld);
2532 results->Add((*results)[results->length() - 2]);
2533 results->Add((*results)[results->length() - 2]);
2534 for (intptr_t i = results->length() - 3; i > 1; --i) {
2535 (*results)[i] = (*results)[i - 2];
2536 }
2537 (*results)[0] = kSmiCid;
2538 (*results)[1] = smi_is_subtype;
2539 }
2540
2541 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded());
2542 ASSERT(results->length() >= 2);
2543 if (type.IsIntType()) {
2544 ASSERT((*results)[0] == kSmiCid);
2545 TryAddTest(results, kMintCid, true);
2546 TryAddTest(results, kBigintCid, true);
2547 // Cannot deoptimize since all tests returning true have been added.
2548 return false;
2549 }
2550
2551 return true; // May deoptimize since we have not identified all 'true' tests.
2552 }
2553
2554
2555 // TODO(srdjan): Use ICData to check if always true or false.
2556 void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
2557 ASSERT(Token::IsTypeTestOperator(call->token_kind()));
2558 Definition* left = call->ArgumentAt(0);
2559 Definition* type_args = NULL;
2560 AbstractType& type = AbstractType::ZoneHandle(Z);
2561 bool negate = false;
2562 if (call->ArgumentCount() == 2) {
2563 type_args = flow_graph()->constant_null();
2564 if (call->function_name().raw() ==
2565 Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) {
2566 type = Type::Number();
2567 } else if (call->function_name().raw() ==
2568 Library::PrivateCoreLibName(Symbols::_instanceOfInt()).raw()) {
2569 type = Type::IntType();
2570 } else if (call->function_name().raw() ==
2571 Library::PrivateCoreLibName(Symbols::_instanceOfSmi()).raw()) {
2572 type = Type::SmiType();
2573 } else if (call->function_name().raw() ==
2574 Library::PrivateCoreLibName(Symbols::_instanceOfDouble()).raw()) {
2575 type = Type::Double();
2576 } else if (call->function_name().raw() ==
2577 Library::PrivateCoreLibName(Symbols::_instanceOfString()).raw()) {
2578 type = Type::StringType();
2579 } else {
2580 UNIMPLEMENTED();
2581 }
2582 negate = Bool::Cast(call->ArgumentAt(1)->OriginalDefinition()
2583 ->AsConstant()->value()).value();
2584 } else {
2585 type_args = call->ArgumentAt(1);
2586 type = AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()).raw();
2587 negate = Bool::Cast(call->ArgumentAt(3)->OriginalDefinition()
2588 ->AsConstant()->value()).value();
2589 }
2590 const ICData& unary_checks =
2591 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
2592 if ((unary_checks.NumberOfChecks() > 0) &&
2593 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) {
2594 ZoneGrowableArray<intptr_t>* results =
2595 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
2596 Bool& as_bool =
2597 Bool::ZoneHandle(Z, InstanceOfAsBool(unary_checks, type, results));
2598 if (as_bool.IsNull()) {
2599 if (results->length() == unary_checks.NumberOfChecks() * 2) {
2600 const bool can_deopt = TryExpandTestCidsResult(results, type);
2601 TestCidsInstr* test_cids = new(Z) TestCidsInstr(
2602 call->token_pos(),
2603 negate ? Token::kISNOT : Token::kIS,
2604 new(Z) Value(left),
2605 *results,
2606 can_deopt ? call->deopt_id() : Thread::kNoDeoptId);
2607 // Remove type.
2608 ReplaceCall(call, test_cids);
2609 return;
2610 }
2611 } else {
2612 // TODO(srdjan): Use TestCidsInstr also for this case.
2613 // One result only.
2614 AddReceiverCheck(call);
2615 if (negate) {
2616 as_bool = Bool::Get(!as_bool.value()).raw();
2617 }
2618 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool);
2619 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
2620 PushArgumentInstr* push = call->PushArgumentAt(i);
2621 push->ReplaceUsesWith(push->value()->definition());
2622 push->RemoveFromGraph();
2623 }
2624 call->ReplaceUsesWith(bool_const);
2625 ASSERT(current_iterator()->Current() == call);
2626 current_iterator()->RemoveCurrentFromGraph();
2627 return;
2628 }
2629 }
2630
2631 if (TypeCheckAsClassEquality(type)) {
2632 LoadClassIdInstr* left_cid = new(Z) LoadClassIdInstr(new(Z) Value(left));
2633 InsertBefore(call,
2634 left_cid,
2635 NULL,
2636 FlowGraph::kValue);
2637 const intptr_t type_cid = Class::Handle(Z, type.type_class()).id();
2638 ConstantInstr* cid =
2639 flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(type_cid)));
2640
2641 StrictCompareInstr* check_cid =
2642 new(Z) StrictCompareInstr(
2643 call->token_pos(),
2644 negate ? Token::kNE_STRICT : Token::kEQ_STRICT,
2645 new(Z) Value(left_cid),
2646 new(Z) Value(cid),
2647 false); // No number check.
2648 ReplaceCall(call, check_cid);
2649 return;
2650 }
2651
2652 InstanceOfInstr* instance_of =
2653 new(Z) InstanceOfInstr(call->token_pos(),
2654 new(Z) Value(left),
2655 new(Z) Value(type_args),
2656 type,
2657 negate,
2658 call->deopt_id());
2659 ReplaceCall(call, instance_of);
2660 }
2661
2662
2663 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids).
2664 void FlowGraphOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) {
2665 ASSERT(Token::IsTypeCastOperator(call->token_kind()));
2666 Definition* left = call->ArgumentAt(0);
2667 Definition* type_args = call->ArgumentAt(1);
2668 const AbstractType& type =
2669 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value());
2670 ASSERT(!type.IsMalformedOrMalbounded());
2671 const ICData& unary_checks =
2672 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
2673 if ((unary_checks.NumberOfChecks() > 0) &&
2674 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) {
2675 ZoneGrowableArray<intptr_t>* results =
2676 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
2677 const Bool& as_bool = Bool::ZoneHandle(Z,
2678 InstanceOfAsBool(unary_checks, type, results));
2679 if (as_bool.raw() == Bool::True().raw()) {
2680 AddReceiverCheck(call);
2681 // Remove the original push arguments.
2682 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
2683 PushArgumentInstr* push = call->PushArgumentAt(i);
2684 push->ReplaceUsesWith(push->value()->definition());
2685 push->RemoveFromGraph();
2686 }
2687 // Remove call, replace it with 'left'.
2688 call->ReplaceUsesWith(left);
2689 ASSERT(current_iterator()->Current() == call);
2690 current_iterator()->RemoveCurrentFromGraph();
2691 return;
2692 }
2693 }
2694 const String& dst_name = String::ZoneHandle(Z,
2695 Symbols::New(Exceptions::kCastErrorDstName));
2696 AssertAssignableInstr* assert_as =
2697 new(Z) AssertAssignableInstr(call->token_pos(),
2698 new(Z) Value(left),
2699 new(Z) Value(type_args),
2700 type,
2701 dst_name,
2702 call->deopt_id());
2703 ReplaceCall(call, assert_as);
2704 }
2705
2706
2707 bool FlowGraphOptimizer::IsBlackListedForInlining(intptr_t call_deopt_id) {
2708 for (intptr_t i = 0; i < inlining_black_list_->length(); ++i) {
2709 if ((*inlining_black_list_)[i] == call_deopt_id) return true;
2710 }
2711 return false;
2712 }
2713
2714 // Special optimizations when running in --noopt mode.
2715 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) {
2716 // TODO(srdjan): Investigate other attempts, as they are not allowed to
2717 // deoptimize.
2718
2719 // Type test is special as it always gets converted into inlined code.
2720 const Token::Kind op_kind = instr->token_kind();
2721 if (Token::IsTypeTestOperator(op_kind)) {
2722 ReplaceWithInstanceOf(instr);
2723 return;
2724 }
2725 if (Token::IsTypeCastOperator(op_kind)) {
2726 ReplaceWithTypeCast(instr);
2727 return;
2728 }
2729
2730 if ((op_kind == Token::kGET) &&
2731 TryInlineInstanceGetter(instr, false /* no checks allowed */)) {
2732 return;
2733 }
2734 const ICData& unary_checks =
2735 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks());
2736 if ((unary_checks.NumberOfChecks() > 0) &&
2737 (op_kind == Token::kSET) &&
2738 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) {
2739 return;
2740 }
2741
2742 if (use_speculative_inlining_ &&
2743 !IsBlackListedForInlining(instr->deopt_id()) &&
2744 (unary_checks.NumberOfChecks() > 0)) {
2745 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) {
2746 return;
2747 }
2748 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) {
2749 return;
2750 }
2751 if ((op_kind == Token::kEQ) && TryReplaceWithEqualityOp(instr, op_kind)) {
2752 return;
2753 }
2754
2755 if (Token::IsRelationalOperator(op_kind) &&
2756 TryReplaceWithRelationalOp(instr, op_kind)) {
2757 return;
2758 }
2759
2760 if (Token::IsBinaryOperator(op_kind) &&
2761 TryReplaceWithBinaryOp(instr, op_kind)) {
2762 return;
2763 }
2764 if (Token::IsUnaryOperator(op_kind) &&
2765 TryReplaceWithUnaryOp(instr, op_kind)) {
2766 return;
2767 }
2768 }
2769
2770 bool has_one_target =
2771 (unary_checks.NumberOfChecks() > 0) && unary_checks.HasOneTarget();
2772 if (has_one_target) {
2773 // Check if the single target is a polymorphic target, if it is,
2774 // we don't have one target.
2775 const Function& target =
2776 Function::Handle(Z, unary_checks.GetTargetAt(0));
2777 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target);
2778 has_one_target = !polymorphic_target;
2779 }
2780
2781 if (has_one_target) {
2782 RawFunction::Kind function_kind =
2783 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind();
2784 if (!InstanceCallNeedsClassCheck(instr, function_kind)) {
2785 PolymorphicInstanceCallInstr* call =
2786 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks,
2787 /* with_checks = */ false);
2788 instr->ReplaceWith(call, current_iterator());
2789 return;
2790 }
2791 }
2792
2793 // More than one targets. Generate generic polymorphic call without
2794 // deoptimization.
2795 if (instr->ic_data()->NumberOfUsedChecks() > 0) {
2796 ASSERT(!FLAG_polymorphic_with_deopt);
2797 // OK to use checks with PolymorphicInstanceCallInstr since no
2798 // deoptimization is allowed.
2799 PolymorphicInstanceCallInstr* call =
2800 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks,
2801 /* with_checks = */ true);
2802 instr->ReplaceWith(call, current_iterator());
2803 return;
2804 }
2805
2806 // No IC data checks. Try resolve target using the propagated type.
2807 // If the propagated type has a method with the target name and there are
2808 // no overrides with that name according to CHA, call the method directly.
2809 const intptr_t receiver_cid =
2810 instr->PushArgumentAt(0)->value()->Type()->ToCid();
2811 if (receiver_cid == kDynamicCid) return;
2812 const Class& receiver_class = Class::Handle(Z,
2813 isolate()->class_table()->At(receiver_cid));
2814
2815 const Array& args_desc_array = Array::Handle(Z,
2816 ArgumentsDescriptor::New(instr->ArgumentCount(),
2817 instr->argument_names()));
2818 ArgumentsDescriptor args_desc(args_desc_array);
2819 const Function& function = Function::Handle(Z,
2820 Resolver::ResolveDynamicForReceiverClass(
2821 receiver_class,
2822 instr->function_name(),
2823 args_desc));
2824 if (function.IsNull()) {
2825 return;
2826 }
2827 if (!thread()->cha()->HasOverride(receiver_class, instr->function_name())) {
2828 if (FLAG_trace_cha) {
2829 THR_Print(" **(CHA) Instance call needs no check, "
2830 "no overrides of '%s' '%s'\n",
2831 instr->function_name().ToCString(), receiver_class.ToCString());
2832 }
2833 thread()->cha()->AddToLeafClasses(receiver_class);
2834
2835 // Create fake IC data with the resolved target.
2836 const ICData& ic_data = ICData::Handle(
2837 ICData::New(flow_graph_->function(),
2838 instr->function_name(),
2839 args_desc_array,
2840 Thread::kNoDeoptId,
2841 /* args_tested = */ 1));
2842 ic_data.AddReceiverCheck(receiver_class.id(), function);
2843 PolymorphicInstanceCallInstr* call =
2844 new(Z) PolymorphicInstanceCallInstr(instr, ic_data,
2845 /* with_checks = */ false);
2846 instr->ReplaceWith(call, current_iterator());
2847 return;
2848 }
2849 }
2850
2851
2852 // Tries to optimize instance call by replacing it with a faster instruction
2853 // (e.g, binary op, field load, ..).
2854 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) {
2855 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) {
2856 return;
2857 }
2858 const Token::Kind op_kind = instr->token_kind();
2859
2860 // Type test is special as it always gets converted into inlined code.
2861 if (Token::IsTypeTestOperator(op_kind)) {
2862 ReplaceWithInstanceOf(instr);
2863 return;
2864 }
2865
2866 if (Token::IsTypeCastOperator(op_kind)) {
2867 ReplaceWithTypeCast(instr);
2868 return;
2869 }
2870
2871 const ICData& unary_checks =
2872 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks());
2873
2874 const intptr_t max_checks = (op_kind == Token::kEQ)
2875 ? FLAG_max_equality_polymorphic_checks
2876 : FLAG_max_polymorphic_checks;
2877 if ((unary_checks.NumberOfChecks() > max_checks) &&
2878 InstanceCallNeedsClassCheck(instr, RawFunction::kRegularFunction)) {
2879 // Too many checks, it will be megamorphic which needs unary checks.
2880 instr->set_ic_data(&unary_checks);
2881 return;
2882 }
2883
2884 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) {
2885 return;
2886 }
2887 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) {
2888 return;
2889 }
2890
2891 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) {
2892 return;
2893 }
2894
2895 if (Token::IsRelationalOperator(op_kind) &&
2896 TryReplaceWithRelationalOp(instr, op_kind)) {
2897 return;
2898 }
2899
2900 if (Token::IsBinaryOperator(op_kind) &&
2901 TryReplaceWithBinaryOp(instr, op_kind)) {
2902 return;
2903 }
2904 if (Token::IsUnaryOperator(op_kind) &&
2905 TryReplaceWithUnaryOp(instr, op_kind)) {
2906 return;
2907 }
2908 if ((op_kind == Token::kGET) && TryInlineInstanceGetter(instr)) {
2909 return;
2910 }
2911 if ((op_kind == Token::kSET) &&
2912 TryInlineInstanceSetter(instr, unary_checks)) {
2913 return;
2914 }
2915 if (TryInlineInstanceMethod(instr)) {
2916 return;
2917 }
2918
2919 bool has_one_target = unary_checks.HasOneTarget();
2920
2921 if (has_one_target) {
2922 // Check if the single target is a polymorphic target, if it is,
2923 // we don't have one target.
2924 const Function& target =
2925 Function::Handle(Z, unary_checks.GetTargetAt(0));
2926 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target);
2927 has_one_target = !polymorphic_target;
2928 }
2929
2930 if (has_one_target) {
2931 RawFunction::Kind function_kind =
2932 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind();
2933 if (!InstanceCallNeedsClassCheck(instr, function_kind)) {
2934 PolymorphicInstanceCallInstr* call =
2935 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks,
2936 /* call_with_checks = */ false);
2937 instr->ReplaceWith(call, current_iterator());
2938 return;
2939 }
2940 }
2941
2942 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
2943 bool call_with_checks;
2944 if (has_one_target && FLAG_polymorphic_with_deopt) {
2945 // Type propagation has not run yet, we cannot eliminate the check.
2946 AddReceiverCheck(instr);
2947 // Call can still deoptimize, do not detach environment from instr.
2948 call_with_checks = false;
2949 } else {
2950 call_with_checks = true;
2951 }
2952 PolymorphicInstanceCallInstr* call =
2953 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks,
2954 call_with_checks);
2955 instr->ReplaceWith(call, current_iterator());
2956 }
2957 }
2958
2959
2960 void FlowGraphOptimizer::VisitStaticCall(StaticCallInstr* call) {
2961 if (!CanUnboxDouble()) {
2962 return;
2963 }
2964 MethodRecognizer::Kind recognized_kind =
2965 MethodRecognizer::RecognizeKind(call->function());
2966 MathUnaryInstr::MathUnaryKind unary_kind;
2967 switch (recognized_kind) {
2968 case MethodRecognizer::kMathSqrt:
2969 unary_kind = MathUnaryInstr::kSqrt;
2970 break;
2971 case MethodRecognizer::kMathSin:
2972 unary_kind = MathUnaryInstr::kSin;
2973 break;
2974 case MethodRecognizer::kMathCos:
2975 unary_kind = MathUnaryInstr::kCos;
2976 break;
2977 default:
2978 unary_kind = MathUnaryInstr::kIllegal;
2979 break;
2980 }
2981 if (unary_kind != MathUnaryInstr::kIllegal) {
2982 MathUnaryInstr* math_unary =
2983 new(Z) MathUnaryInstr(unary_kind,
2984 new(Z) Value(call->ArgumentAt(0)),
2985 call->deopt_id());
2986 ReplaceCall(call, math_unary);
2987 return;
2988 }
2989 switch (recognized_kind) {
2990 case MethodRecognizer::kFloat32x4Zero:
2991 case MethodRecognizer::kFloat32x4Splat:
2992 case MethodRecognizer::kFloat32x4Constructor:
2993 case MethodRecognizer::kFloat32x4FromFloat64x2:
2994 TryInlineFloat32x4Constructor(call, recognized_kind);
2995 break;
2996 case MethodRecognizer::kFloat64x2Constructor:
2997 case MethodRecognizer::kFloat64x2Zero:
2998 case MethodRecognizer::kFloat64x2Splat:
2999 case MethodRecognizer::kFloat64x2FromFloat32x4:
3000 TryInlineFloat64x2Constructor(call, recognized_kind);
3001 break;
3002 case MethodRecognizer::kInt32x4BoolConstructor:
3003 case MethodRecognizer::kInt32x4Constructor:
3004 TryInlineInt32x4Constructor(call, recognized_kind);
3005 break;
3006 case MethodRecognizer::kObjectConstructor: {
3007 // Remove the original push arguments.
3008 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
3009 PushArgumentInstr* push = call->PushArgumentAt(i);
3010 push->ReplaceUsesWith(push->value()->definition());
3011 push->RemoveFromGraph();
3012 }
3013 // Manually replace call with global null constant. ReplaceCall can't
3014 // be used for definitions that are already in the graph.
3015 call->ReplaceUsesWith(flow_graph_->constant_null());
3016 ASSERT(current_iterator()->Current() == call);
3017 current_iterator()->RemoveCurrentFromGraph();
3018 break;
3019 }
3020 case MethodRecognizer::kMathMin:
3021 case MethodRecognizer::kMathMax: {
3022 // We can handle only monomorphic min/max call sites with both arguments
3023 // being either doubles or smis.
3024 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) {
3025 const ICData& ic_data = *call->ic_data();
3026 intptr_t result_cid = kIllegalCid;
3027 if (ICDataHasReceiverArgumentClassIds(ic_data,
3028 kDoubleCid, kDoubleCid)) {
3029 result_cid = kDoubleCid;
3030 } else if (ICDataHasReceiverArgumentClassIds(ic_data,
3031 kSmiCid, kSmiCid)) {
3032 result_cid = kSmiCid;
3033 }
3034 if (result_cid != kIllegalCid) {
3035 MathMinMaxInstr* min_max = new(Z) MathMinMaxInstr(
3036 recognized_kind,
3037 new(Z) Value(call->ArgumentAt(0)),
3038 new(Z) Value(call->ArgumentAt(1)),
3039 call->deopt_id(),
3040 result_cid);
3041 const ICData& unary_checks =
3042 ICData::ZoneHandle(Z, ic_data.AsUnaryClassChecks());
3043 AddCheckClass(min_max->left()->definition(),
3044 unary_checks,
3045 call->deopt_id(),
3046 call->env(),
3047 call);
3048 AddCheckClass(min_max->right()->definition(),
3049 unary_checks,
3050 call->deopt_id(),
3051 call->env(),
3052 call);
3053 ReplaceCall(call, min_max);
3054 }
3055 }
3056 break;
3057 }
3058 case MethodRecognizer::kMathDoublePow:
3059 case MethodRecognizer::kMathTan:
3060 case MethodRecognizer::kMathAsin:
3061 case MethodRecognizer::kMathAcos:
3062 case MethodRecognizer::kMathAtan:
3063 case MethodRecognizer::kMathAtan2: {
3064 // InvokeMathCFunctionInstr requires unboxed doubles. UnboxDouble
3065 // instructions contain type checks and conversions to double.
3066 ZoneGrowableArray<Value*>* args =
3067 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount());
3068 for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
3069 args->Add(new(Z) Value(call->ArgumentAt(i)));
3070 }
3071 InvokeMathCFunctionInstr* invoke =
3072 new(Z) InvokeMathCFunctionInstr(args,
3073 call->deopt_id(),
3074 recognized_kind,
3075 call->token_pos());
3076 ReplaceCall(call, invoke);
3077 break;
3078 }
3079 case MethodRecognizer::kDoubleFromInteger: {
3080 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) {
3081 const ICData& ic_data = *call->ic_data();
3082 if (CanUnboxDouble()) {
3083 if (ArgIsAlways(kSmiCid, ic_data, 1)) {
3084 Definition* arg = call->ArgumentAt(1);
3085 AddCheckSmi(arg, call->deopt_id(), call->env(), call);
3086 ReplaceCall(call,
3087 new(Z) SmiToDoubleInstr(new(Z) Value(arg),
3088 call->token_pos()));
3089 } else if (ArgIsAlways(kMintCid, ic_data, 1) &&
3090 CanConvertUnboxedMintToDouble()) {
3091 Definition* arg = call->ArgumentAt(1);
3092 ReplaceCall(call,
3093 new(Z) MintToDoubleInstr(new(Z) Value(arg),
3094 call->deopt_id()));
3095 }
3096 }
3097 }
3098 break;
3099 }
3100 default: {
3101 if (call->function().IsFactory()) {
3102 const Class& function_class =
3103 Class::Handle(Z, call->function().Owner());
3104 if ((function_class.library() == Library::CoreLibrary()) ||
3105 (function_class.library() == Library::TypedDataLibrary())) {
3106 intptr_t cid = FactoryRecognizer::ResultCid(call->function());
3107 switch (cid) {
3108 case kArrayCid: {
3109 Value* type = new(Z) Value(call->ArgumentAt(0));
3110 Value* num_elements = new(Z) Value(call->ArgumentAt(1));
3111 if (num_elements->BindsToConstant() &&
3112 num_elements->BoundConstant().IsSmi()) {
3113 intptr_t length =
3114 Smi::Cast(num_elements->BoundConstant()).Value();
3115 if (length >= 0 && length <= Array::kMaxElements) {
3116 CreateArrayInstr* create_array =
3117 new(Z) CreateArrayInstr(
3118 call->token_pos(), type, num_elements);
3119 ReplaceCall(call, create_array);
3120 }
3121 }
3122 }
3123 default:
3124 break;
3125 }
3126 }
3127 }
3128 }
3129 }
3130 }
3131
3132
3133 void FlowGraphOptimizer::VisitStoreInstanceField(
3134 StoreInstanceFieldInstr* instr) {
3135 if (instr->IsUnboxedStore()) {
3136 ASSERT(instr->is_potential_unboxed_initialization_);
3137 // Determine if this field should be unboxed based on the usage of getter
3138 // and setter functions: The heuristic requires that the setter has a
3139 // usage count of at least 1/kGetterSetterRatio of the getter usage count.
3140 // This is to avoid unboxing fields where the setter is never or rarely
3141 // executed.
3142 const Field& field = Field::ZoneHandle(Z, instr->field().raw());
3143 const String& field_name = String::Handle(Z, field.name());
3144 const Class& owner = Class::Handle(Z, field.owner());
3145 const Function& getter =
3146 Function::Handle(Z, owner.LookupGetterFunction(field_name));
3147 const Function& setter =
3148 Function::Handle(Z, owner.LookupSetterFunction(field_name));
3149 bool unboxed_field = false;
3150 if (!getter.IsNull() && !setter.IsNull()) {
3151 if (field.is_double_initialized()) {
3152 unboxed_field = true;
3153 } else if ((setter.usage_counter() > 0) &&
3154 ((FLAG_getter_setter_ratio * setter.usage_counter()) >=
3155 getter.usage_counter())) {
3156 unboxed_field = true;
3157 }
3158 }
3159 if (!unboxed_field) {
3160 // TODO(srdjan): Instead of aborting pass this field to the mutator thread
3161 // so that it can:
3162 // - set it to unboxed
3163 // - deoptimize dependent code.
3164 if (Compiler::IsBackgroundCompilation()) {
3165 isolate()->AddDeoptimizingBoxedField(field);
3166 Compiler::AbortBackgroundCompilation(Thread::kNoDeoptId);
3167 UNREACHABLE();
3168 }
3169 if (FLAG_trace_optimization || FLAG_trace_field_guards) {
3170 THR_Print("Disabling unboxing of %s\n", field.ToCString());
3171 if (!setter.IsNull()) {
3172 OS::Print(" setter usage count: %" Pd "\n", setter.usage_counter());
3173 }
3174 if (!getter.IsNull()) {
3175 OS::Print(" getter usage count: %" Pd "\n", getter.usage_counter());
3176 }
3177 }
3178 field.set_is_unboxing_candidate(false);
3179 field.DeoptimizeDependentCode();
3180 } else {
3181 flow_graph()->parsed_function().AddToGuardedFields(&field);
3182 }
3183 }
3184 }
3185
3186
3187 void FlowGraphOptimizer::VisitAllocateContext(AllocateContextInstr* instr) {
3188 // Replace generic allocation with a sequence of inlined allocation and
3189 // explicit initalizing stores.
3190 AllocateUninitializedContextInstr* replacement =
3191 new AllocateUninitializedContextInstr(instr->token_pos(),
3192 instr->num_context_variables());
3193 instr->ReplaceWith(replacement, current_iterator());
3194
3195 StoreInstanceFieldInstr* store =
3196 new(Z) StoreInstanceFieldInstr(Context::parent_offset(),
3197 new Value(replacement),
3198 new Value(flow_graph_->constant_null()),
3199 kNoStoreBarrier,
3200 instr->token_pos());
3201 // Storing into uninitialized memory; remember to prevent dead store
3202 // elimination and ensure proper GC barrier.
3203 store->set_is_object_reference_initialization(true);
3204 flow_graph_->InsertAfter(replacement, store, NULL, FlowGraph::kEffect);
3205 Definition* cursor = store;
3206 for (intptr_t i = 0; i < instr->num_context_variables(); ++i) {
3207 store =
3208 new(Z) StoreInstanceFieldInstr(Context::variable_offset(i),
3209 new Value(replacement),
3210 new Value(flow_graph_->constant_null()),
3211 kNoStoreBarrier,
3212 instr->token_pos());
3213 // Storing into uninitialized memory; remember to prevent dead store
3214 // elimination and ensure proper GC barrier.
3215 store->set_is_object_reference_initialization(true);
3216 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect);
3217 cursor = store;
3218 }
3219 }
3220
3221
3222 void FlowGraphOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) {
3223 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized.
3224 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM)
3225 if (!instr->can_pack_into_smi())
3226 instr->set_representation(kUnboxedMint);
3227 #endif
3228 }
3229
3230
3231 bool FlowGraphOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr,
3232 const ICData& unary_ic_data,
3233 bool allow_checks) {
3234 ASSERT((unary_ic_data.NumberOfChecks() > 0) &&
3235 (unary_ic_data.NumArgsTested() == 1));
3236 if (I->flags().type_checks()) {
3237 // Checked mode setters are inlined like normal methods by conventional
3238 // inlining.
3239 return false;
3240 }
3241
3242 ASSERT(instr->HasICData());
3243 if (unary_ic_data.NumberOfChecks() == 0) {
3244 // No type feedback collected.
3245 return false;
3246 }
3247 if (!unary_ic_data.HasOneTarget()) {
3248 // Polymorphic sites are inlined like normal method calls by conventional
3249 // inlining.
3250 return false;
3251 }
3252 Function& target = Function::Handle(Z);
3253 intptr_t class_id;
3254 unary_ic_data.GetOneClassCheckAt(0, &class_id, &target);
3255 if (target.kind() != RawFunction::kImplicitSetter) {
3256 // Non-implicit setter are inlined like normal method calls.
3257 return false;
3258 }
3259 // Inline implicit instance setter.
3260 const String& field_name =
3261 String::Handle(Z, Field::NameFromSetter(instr->function_name()));
3262 const Field& field =
3263 Field::ZoneHandle(Z, GetField(class_id, field_name));
3264 ASSERT(!field.IsNull());
3265
3266 if (InstanceCallNeedsClassCheck(instr, RawFunction::kImplicitSetter)) {
3267 if (!allow_checks) {
3268 return false;
3269 }
3270 AddReceiverCheck(instr);
3271 }
3272 if (field.guarded_cid() != kDynamicCid) {
3273 if (!allow_checks) {
3274 return false;
3275 }
3276 InsertBefore(instr,
3277 new(Z) GuardFieldClassInstr(
3278 new(Z) Value(instr->ArgumentAt(1)),
3279 field,
3280 instr->deopt_id()),
3281 instr->env(),
3282 FlowGraph::kEffect);
3283 }
3284
3285 if (field.needs_length_check()) {
3286 if (!allow_checks) {
3287 return false;
3288 }
3289 InsertBefore(instr,
3290 new(Z) GuardFieldLengthInstr(
3291 new(Z) Value(instr->ArgumentAt(1)),
3292 field,
3293 instr->deopt_id()),
3294 instr->env(),
3295 FlowGraph::kEffect);
3296 }
3297
3298 // Field guard was detached.
3299 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr(
3300 field,
3301 new(Z) Value(instr->ArgumentAt(0)),
3302 new(Z) Value(instr->ArgumentAt(1)),
3303 kEmitStoreBarrier,
3304 instr->token_pos());
3305
3306 if (store->IsUnboxedStore()) {
3307 flow_graph()->parsed_function().AddToGuardedFields(&field);
3308 }
3309
3310 // Discard the environment from the original instruction because the store
3311 // can't deoptimize.
3312 instr->RemoveEnvironment();
3313 ReplaceCall(instr, store);
3314 return true;
3315 }
3316
3317
3318 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698