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

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

Issue 820883004: Move ConstantPropagator from flow_graph_optimizer.cc/.h into separate files. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 5 years, 11 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 | Annotate | Revision Log
« no previous file with comments | « runtime/vm/constant_propagator.h ('k') | runtime/vm/flow_graph_optimizer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/flow_graph_optimizer.h" 5 #include "vm/constant_propagator.h"
6 6
7 #include "vm/bit_vector.h" 7 #include "vm/bit_vector.h"
8 #include "vm/cha.h"
9 #include "vm/cpu.h"
10 #include "vm/dart_entry.h"
11 #include "vm/exceptions.h"
12 #include "vm/flow_graph_builder.h" 8 #include "vm/flow_graph_builder.h"
13 #include "vm/flow_graph_compiler.h" 9 #include "vm/flow_graph_compiler.h"
14 #include "vm/flow_graph_range_analysis.h" 10 #include "vm/flow_graph_range_analysis.h"
15 #include "vm/hash_map.h"
16 #include "vm/il_printer.h" 11 #include "vm/il_printer.h"
17 #include "vm/intermediate_language.h" 12 #include "vm/intermediate_language.h"
18 #include "vm/object_store.h"
19 #include "vm/parser.h" 13 #include "vm/parser.h"
20 #include "vm/resolver.h"
21 #include "vm/scopes.h"
22 #include "vm/stack_frame.h"
23 #include "vm/symbols.h" 14 #include "vm/symbols.h"
24 15
25 namespace dart { 16 namespace dart {
26 17
27 DEFINE_FLAG(int, getter_setter_ratio, 13,
28 "Ratio of getter/setter usage used for double field unboxing heuristics");
29 DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination.");
30 DEFINE_FLAG(bool, dead_store_elimination, true, "Eliminate dead stores");
31 DEFINE_FLAG(int, max_polymorphic_checks, 4,
32 "Maximum number of polymorphic check, otherwise it is megamorphic.");
33 DEFINE_FLAG(int, max_equality_polymorphic_checks, 32,
34 "Maximum number of polymorphic checks in equality operator,"
35 " otherwise use megamorphic dispatch.");
36 DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis."); 18 DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis.");
37 DEFINE_FLAG(bool, trace_constant_propagation, false, 19 DEFINE_FLAG(bool, trace_constant_propagation, false,
38 "Print constant propagation and useless code elimination."); 20 "Print constant propagation and useless code elimination.");
39 DEFINE_FLAG(bool, trace_load_optimization, false,
40 "Print live sets for load optimization pass.");
41 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details.");
42 DEFINE_FLAG(bool, truncating_left_shift, true,
43 "Optimize left shift to truncate if possible");
44 DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis.");
45 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32)
46 DEFINE_FLAG(bool, trace_smi_widening, false, "Trace Smi->Int32 widening pass.");
47 #endif
48 DECLARE_FLAG(bool, enable_type_checks);
49 DECLARE_FLAG(bool, source_lines);
50 DECLARE_FLAG(bool, trace_type_check_elimination);
51 DECLARE_FLAG(bool, warn_on_javascript_compatibility);
52 21
53 // Quick access to the locally defined isolate() method. 22 // Quick access to the locally defined isolate() method.
54 #define I (isolate()) 23 #define I (isolate())
55 24
56 static bool ShouldInlineSimd() {
57 return FlowGraphCompiler::SupportsUnboxedSimd128();
58 }
59
60
61 static bool CanUnboxDouble() {
62 return FlowGraphCompiler::SupportsUnboxedDoubles();
63 }
64
65
66 static bool ShouldInlineInt64ArrayOps() {
67 #if defined(TARGET_ARCH_X64)
68 return true;
69 #endif
70 return false;
71 }
72
73 static bool CanConvertUnboxedMintToDouble() {
74 #if defined(TARGET_ARCH_IA32)
75 return true;
76 #else
77 // ARM does not have a short instruction sequence for converting int64 to
78 // double.
79 // TODO(johnmccutchan): Investigate possibility on MIPS once
80 // mints are implemented there.
81 return false;
82 #endif
83 }
84
85
86 // Optimize instance calls using ICData.
87 void FlowGraphOptimizer::ApplyICData() {
88 VisitBlocks();
89 }
90
91
92 // Optimize instance calls using cid. This is called after optimizer
93 // converted instance calls to instructions. Any remaining
94 // instance calls are either megamorphic calls, cannot be optimized or
95 // have no runtime type feedback collected.
96 // Attempts to convert an instance call (IC call) using propagated class-ids,
97 // e.g., receiver class id, guarded-cid, or by guessing cid-s.
98 void FlowGraphOptimizer::ApplyClassIds() {
99 ASSERT(current_iterator_ == NULL);
100 for (intptr_t i = 0; i < block_order_.length(); ++i) {
101 BlockEntryInstr* entry = block_order_[i];
102 ForwardInstructionIterator it(entry);
103 current_iterator_ = &it;
104 for (; !it.Done(); it.Advance()) {
105 Instruction* instr = it.Current();
106 if (instr->IsInstanceCall()) {
107 InstanceCallInstr* call = instr->AsInstanceCall();
108 if (call->HasICData()) {
109 if (TryCreateICData(call)) {
110 VisitInstanceCall(call);
111 }
112 }
113 } else if (instr->IsPolymorphicInstanceCall()) {
114 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall());
115 } else if (instr->IsStrictCompare()) {
116 VisitStrictCompare(instr->AsStrictCompare());
117 } else if (instr->IsBranch()) {
118 ComparisonInstr* compare = instr->AsBranch()->comparison();
119 if (compare->IsStrictCompare()) {
120 VisitStrictCompare(compare->AsStrictCompare());
121 }
122 }
123 }
124 current_iterator_ = NULL;
125 }
126 }
127
128
129 // TODO(srdjan): Test/support other number types as well.
130 static bool IsNumberCid(intptr_t cid) {
131 return (cid == kSmiCid) || (cid == kDoubleCid);
132 }
133
134
135 // Attempt to build ICData for call using propagated class-ids.
136 bool FlowGraphOptimizer::TryCreateICData(InstanceCallInstr* call) {
137 ASSERT(call->HasICData());
138 if (call->ic_data()->NumberOfUsedChecks() > 0) {
139 // This occurs when an instance call has too many checks, will be converted
140 // to megamorphic call.
141 return false;
142 }
143 if (FLAG_warn_on_javascript_compatibility) {
144 // Do not make the instance call megamorphic if the callee needs to decode
145 // the calling code sequence to lookup the ic data and verify if a warning
146 // has already been issued or not.
147 // TryCreateICData is only invoked if the ic_data target has not been called
148 // yet, so no warning can possibly have been issued.
149 ASSERT(!call->ic_data()->IssuedJSWarning());
150 if (call->ic_data()->MayCheckForJSWarning()) {
151 return false;
152 }
153 }
154 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested());
155 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount());
156 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) {
157 const intptr_t cid = call->PushArgumentAt(i)->value()->Type()->ToCid();
158 class_ids.Add(cid);
159 }
160
161 const Token::Kind op_kind = call->token_kind();
162 if (Token::IsRelationalOperator(op_kind) ||
163 Token::IsEqualityOperator(op_kind) ||
164 Token::IsBinaryOperator(op_kind)) {
165 // Guess cid: if one of the inputs is a number assume that the other
166 // is a number of same type.
167 const intptr_t cid_0 = class_ids[0];
168 const intptr_t cid_1 = class_ids[1];
169 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) {
170 class_ids[0] = cid_1;
171 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) {
172 class_ids[1] = cid_0;
173 }
174 }
175
176 for (intptr_t i = 0; i < class_ids.length(); i++) {
177 if (class_ids[i] == kDynamicCid) {
178 // Not all cid-s known.
179 return false;
180 }
181 }
182
183 const Array& args_desc_array = Array::Handle(I,
184 ArgumentsDescriptor::New(call->ArgumentCount(), call->argument_names()));
185 ArgumentsDescriptor args_desc(args_desc_array);
186 const Class& receiver_class = Class::Handle(I,
187 isolate()->class_table()->At(class_ids[0]));
188 const Function& function = Function::Handle(I,
189 Resolver::ResolveDynamicForReceiverClass(
190 receiver_class,
191 call->function_name(),
192 args_desc));
193 if (function.IsNull()) {
194 return false;
195 }
196 // Create new ICData, do not modify the one attached to the instruction
197 // since it is attached to the assembly instruction itself.
198 // TODO(srdjan): Prevent modification of ICData object that is
199 // referenced in assembly code.
200 ICData& ic_data = ICData::ZoneHandle(I, ICData::New(
201 flow_graph_->parsed_function()->function(),
202 call->function_name(),
203 args_desc_array,
204 call->deopt_id(),
205 class_ids.length()));
206 if (class_ids.length() > 1) {
207 ic_data.AddCheck(class_ids, function);
208 } else {
209 ASSERT(class_ids.length() == 1);
210 ic_data.AddReceiverCheck(class_ids[0], function);
211 }
212 call->set_ic_data(&ic_data);
213 return true;
214 }
215
216
217 const ICData& FlowGraphOptimizer::TrySpecializeICData(const ICData& ic_data,
218 intptr_t cid) {
219 ASSERT(ic_data.NumArgsTested() == 1);
220
221 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) {
222 return ic_data; // Nothing to do
223 }
224
225 const Function& function =
226 Function::Handle(I, ic_data.GetTargetForReceiverClassId(cid));
227 // TODO(fschneider): Try looking up the function on the class if it is
228 // not found in the ICData.
229 if (!function.IsNull()) {
230 const ICData& new_ic_data = ICData::ZoneHandle(I, ICData::New(
231 Function::Handle(I, ic_data.owner()),
232 String::Handle(I, ic_data.target_name()),
233 Object::empty_array(), // Dummy argument descriptor.
234 ic_data.deopt_id(),
235 ic_data.NumArgsTested()));
236 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons());
237 new_ic_data.AddReceiverCheck(cid, function);
238 return new_ic_data;
239 }
240
241 return ic_data;
242 }
243
244
245 void FlowGraphOptimizer::SpecializePolymorphicInstanceCall(
246 PolymorphicInstanceCallInstr* call) {
247 if (!call->with_checks()) {
248 return; // Already specialized.
249 }
250
251 const intptr_t receiver_cid =
252 call->PushArgumentAt(0)->value()->Type()->ToCid();
253 if (receiver_cid == kDynamicCid) {
254 return; // No information about receiver was infered.
255 }
256
257 const ICData& ic_data = TrySpecializeICData(call->ic_data(), receiver_cid);
258 if (ic_data.raw() == call->ic_data().raw()) {
259 // No specialization.
260 return;
261 }
262
263 const bool with_checks = false;
264 PolymorphicInstanceCallInstr* specialized =
265 new(I) PolymorphicInstanceCallInstr(call->instance_call(),
266 ic_data,
267 with_checks);
268 call->ReplaceWith(specialized, current_iterator());
269 }
270
271
272 static BinarySmiOpInstr* AsSmiShiftLeftInstruction(Definition* d) {
273 BinarySmiOpInstr* instr = d->AsBinarySmiOp();
274 if ((instr != NULL) && (instr->op_kind() == Token::kSHL)) {
275 return instr;
276 }
277 return NULL;
278 }
279
280
281 static bool IsPositiveOrZeroSmiConst(Definition* d) {
282 ConstantInstr* const_instr = d->AsConstant();
283 if ((const_instr != NULL) && (const_instr->value().IsSmi())) {
284 return Smi::Cast(const_instr->value()).Value() >= 0;
285 }
286 return false;
287 }
288
289
290 void FlowGraphOptimizer::OptimizeLeftShiftBitAndSmiOp(
291 Definition* bit_and_instr,
292 Definition* left_instr,
293 Definition* right_instr) {
294 ASSERT(bit_and_instr != NULL);
295 ASSERT((left_instr != NULL) && (right_instr != NULL));
296
297 // Check for pattern, smi_shift_left must be single-use.
298 bool is_positive_or_zero = IsPositiveOrZeroSmiConst(left_instr);
299 if (!is_positive_or_zero) {
300 is_positive_or_zero = IsPositiveOrZeroSmiConst(right_instr);
301 }
302 if (!is_positive_or_zero) return;
303
304 BinarySmiOpInstr* smi_shift_left = NULL;
305 if (bit_and_instr->InputAt(0)->IsSingleUse()) {
306 smi_shift_left = AsSmiShiftLeftInstruction(left_instr);
307 }
308 if ((smi_shift_left == NULL) && (bit_and_instr->InputAt(1)->IsSingleUse())) {
309 smi_shift_left = AsSmiShiftLeftInstruction(right_instr);
310 }
311 if (smi_shift_left == NULL) return;
312
313 // Pattern recognized.
314 smi_shift_left->mark_truncating();
315 ASSERT(bit_and_instr->IsBinarySmiOp() || bit_and_instr->IsBinaryMintOp());
316 if (bit_and_instr->IsBinaryMintOp()) {
317 // Replace Mint op with Smi op.
318 BinarySmiOpInstr* smi_op = new(I) BinarySmiOpInstr(
319 Token::kBIT_AND,
320 new(I) Value(left_instr),
321 new(I) Value(right_instr),
322 Isolate::kNoDeoptId); // BIT_AND cannot deoptimize.
323 bit_and_instr->ReplaceWith(smi_op, current_iterator());
324 }
325 }
326
327
328
329 // Used by TryMergeDivMod.
330 // Inserts a load-indexed instruction between a TRUNCDIV or MOD instruction,
331 // and the using instruction. This is an intermediate step before merging.
332 void FlowGraphOptimizer::AppendLoadIndexedForMerged(Definition* instr,
333 intptr_t ix,
334 intptr_t cid) {
335 const intptr_t index_scale = Instance::ElementSizeFor(cid);
336 ConstantInstr* index_instr =
337 flow_graph()->GetConstant(Smi::Handle(I, Smi::New(ix)));
338 LoadIndexedInstr* load =
339 new(I) LoadIndexedInstr(new(I) Value(instr),
340 new(I) Value(index_instr),
341 index_scale,
342 cid,
343 Isolate::kNoDeoptId,
344 instr->token_pos());
345 instr->ReplaceUsesWith(load);
346 flow_graph()->InsertAfter(instr, load, NULL, FlowGraph::kValue);
347 }
348
349
350 void FlowGraphOptimizer::AppendExtractNthOutputForMerged(Definition* instr,
351 intptr_t index,
352 Representation rep,
353 intptr_t cid) {
354 ExtractNthOutputInstr* extract =
355 new(I) ExtractNthOutputInstr(new(I) Value(instr), index, rep, cid);
356 instr->ReplaceUsesWith(extract);
357 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue);
358 }
359
360
361 // Dart:
362 // var x = d % 10;
363 // var y = d ~/ 10;
364 // var z = x + y;
365 //
366 // IL:
367 // v4 <- %(v2, v3)
368 // v5 <- ~/(v2, v3)
369 // v6 <- +(v4, v5)
370 //
371 // IL optimized:
372 // v4 <- DIVMOD(v2, v3);
373 // v5 <- LoadIndexed(v4, 0); // ~/ result
374 // v6 <- LoadIndexed(v4, 1); // % result
375 // v7 <- +(v5, v6)
376 // Because of the environment it is important that merged instruction replaces
377 // first original instruction encountered.
378 void FlowGraphOptimizer::TryMergeTruncDivMod(
379 GrowableArray<BinarySmiOpInstr*>* merge_candidates) {
380 if (merge_candidates->length() < 2) {
381 // Need at least a TRUNCDIV and a MOD.
382 return;
383 }
384 for (intptr_t i = 0; i < merge_candidates->length(); i++) {
385 BinarySmiOpInstr* curr_instr = (*merge_candidates)[i];
386 if (curr_instr == NULL) {
387 // Instruction was merged already.
388 continue;
389 }
390 ASSERT((curr_instr->op_kind() == Token::kTRUNCDIV) ||
391 (curr_instr->op_kind() == Token::kMOD));
392 // Check if there is kMOD/kTRUNDIV binop with same inputs.
393 const intptr_t other_kind = (curr_instr->op_kind() == Token::kTRUNCDIV) ?
394 Token::kMOD : Token::kTRUNCDIV;
395 Definition* left_def = curr_instr->left()->definition();
396 Definition* right_def = curr_instr->right()->definition();
397 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) {
398 BinarySmiOpInstr* other_binop = (*merge_candidates)[k];
399 // 'other_binop' can be NULL if it was already merged.
400 if ((other_binop != NULL) &&
401 (other_binop->op_kind() == other_kind) &&
402 (other_binop->left()->definition() == left_def) &&
403 (other_binop->right()->definition() == right_def)) {
404 (*merge_candidates)[k] = NULL; // Clear it.
405 ASSERT(curr_instr->HasUses());
406 AppendExtractNthOutputForMerged(
407 curr_instr,
408 MergedMathInstr::OutputIndexOf(curr_instr->op_kind()),
409 kTagged, kSmiCid);
410 ASSERT(other_binop->HasUses());
411 AppendExtractNthOutputForMerged(
412 other_binop,
413 MergedMathInstr::OutputIndexOf(other_binop->op_kind()),
414 kTagged, kSmiCid);
415
416 ZoneGrowableArray<Value*>* args = new(I) ZoneGrowableArray<Value*>(2);
417 args->Add(new(I) Value(curr_instr->left()->definition()));
418 args->Add(new(I) Value(curr_instr->right()->definition()));
419
420 // Replace with TruncDivMod.
421 MergedMathInstr* div_mod = new(I) MergedMathInstr(
422 args,
423 curr_instr->deopt_id(),
424 MergedMathInstr::kTruncDivMod);
425 curr_instr->ReplaceWith(div_mod, current_iterator());
426 other_binop->ReplaceUsesWith(div_mod);
427 other_binop->RemoveFromGraph();
428 // Only one merge possible. Because canonicalization happens later,
429 // more candidates are possible.
430 // TODO(srdjan): Allow merging of trunc-div/mod into truncDivMod.
431 break;
432 }
433 }
434 }
435 }
436
437
438 // Tries to merge MathUnary operations, in this case sinus and cosinus.
439 void FlowGraphOptimizer::TryMergeMathUnary(
440 GrowableArray<MathUnaryInstr*>* merge_candidates) {
441 if (!FlowGraphCompiler::SupportsSinCos() || !CanUnboxDouble()) {
442 return;
443 }
444 if (merge_candidates->length() < 2) {
445 // Need at least a SIN and a COS.
446 return;
447 }
448 for (intptr_t i = 0; i < merge_candidates->length(); i++) {
449 MathUnaryInstr* curr_instr = (*merge_candidates)[i];
450 if (curr_instr == NULL) {
451 // Instruction was merged already.
452 continue;
453 }
454 const intptr_t kind = curr_instr->kind();
455 ASSERT((kind == MathUnaryInstr::kSin) ||
456 (kind == MathUnaryInstr::kCos));
457 // Check if there is sin/cos binop with same inputs.
458 const intptr_t other_kind = (kind == MethodRecognizer::kMathSin) ?
459 MethodRecognizer::kMathCos : MethodRecognizer::kMathSin;
460 Definition* def = curr_instr->value()->definition();
461 for (intptr_t k = i + 1; k < merge_candidates->length(); k++) {
462 MathUnaryInstr* other_op = (*merge_candidates)[k];
463 // 'other_op' can be NULL if it was already merged.
464 if ((other_op != NULL) && (other_op->kind() == other_kind) &&
465 (other_op->value()->definition() == def)) {
466 (*merge_candidates)[k] = NULL; // Clear it.
467 ASSERT(curr_instr->HasUses());
468 AppendExtractNthOutputForMerged(curr_instr,
469 MergedMathInstr::OutputIndexOf(kind),
470 kUnboxedDouble, kDoubleCid);
471 ASSERT(other_op->HasUses());
472 AppendExtractNthOutputForMerged(
473 other_op,
474 MergedMathInstr::OutputIndexOf(other_kind),
475 kUnboxedDouble, kDoubleCid);
476 ZoneGrowableArray<Value*>* args = new(I) ZoneGrowableArray<Value*>(1);
477 args->Add(new(I) Value(curr_instr->value()->definition()));
478 // Replace with SinCos.
479 MergedMathInstr* sin_cos =
480 new(I) MergedMathInstr(args,
481 curr_instr->DeoptimizationTarget(),
482 MergedMathInstr::kSinCos);
483 curr_instr->ReplaceWith(sin_cos, current_iterator());
484 other_op->ReplaceUsesWith(sin_cos);
485 other_op->RemoveFromGraph();
486 // Only one merge possible. Because canonicalization happens later,
487 // more candidates are possible.
488 // TODO(srdjan): Allow merging of sin/cos into sincos.
489 break;
490 }
491 }
492 }
493 }
494
495
496 // Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the
497 // shift can be a truncating Smi shift-left and result is always Smi.
498 // Merging occurs only per basic-block.
499 void FlowGraphOptimizer::TryOptimizePatterns() {
500 if (!FLAG_truncating_left_shift) return;
501 ASSERT(current_iterator_ == NULL);
502 GrowableArray<BinarySmiOpInstr*> div_mod_merge;
503 GrowableArray<MathUnaryInstr*> sin_cos_merge;
504 for (intptr_t i = 0; i < block_order_.length(); ++i) {
505 // Merging only per basic-block.
506 div_mod_merge.Clear();
507 sin_cos_merge.Clear();
508 BlockEntryInstr* entry = block_order_[i];
509 ForwardInstructionIterator it(entry);
510 current_iterator_ = &it;
511 for (; !it.Done(); it.Advance()) {
512 if (it.Current()->IsBinarySmiOp()) {
513 BinarySmiOpInstr* binop = it.Current()->AsBinarySmiOp();
514 if (binop->op_kind() == Token::kBIT_AND) {
515 OptimizeLeftShiftBitAndSmiOp(binop,
516 binop->left()->definition(),
517 binop->right()->definition());
518 } else if ((binop->op_kind() == Token::kTRUNCDIV) ||
519 (binop->op_kind() == Token::kMOD)) {
520 if (binop->HasUses()) {
521 div_mod_merge.Add(binop);
522 }
523 }
524 } else if (it.Current()->IsBinaryMintOp()) {
525 BinaryMintOpInstr* mintop = it.Current()->AsBinaryMintOp();
526 if (mintop->op_kind() == Token::kBIT_AND) {
527 OptimizeLeftShiftBitAndSmiOp(mintop,
528 mintop->left()->definition(),
529 mintop->right()->definition());
530 }
531 } else if (it.Current()->IsMathUnary()) {
532 MathUnaryInstr* math_unary = it.Current()->AsMathUnary();
533 if ((math_unary->kind() == MathUnaryInstr::kSin) ||
534 (math_unary->kind() == MathUnaryInstr::kCos)) {
535 if (math_unary->HasUses()) {
536 sin_cos_merge.Add(math_unary);
537 }
538 }
539 }
540 }
541 TryMergeTruncDivMod(&div_mod_merge);
542 TryMergeMathUnary(&sin_cos_merge);
543 current_iterator_ = NULL;
544 }
545 }
546
547
548 static void EnsureSSATempIndex(FlowGraph* graph,
549 Definition* defn,
550 Definition* replacement) {
551 if ((replacement->ssa_temp_index() == -1) &&
552 (defn->ssa_temp_index() != -1)) {
553 replacement->set_ssa_temp_index(graph->alloc_ssa_temp_index());
554 }
555 }
556
557
558 static void ReplaceCurrentInstruction(ForwardInstructionIterator* iterator,
559 Instruction* current,
560 Instruction* replacement,
561 FlowGraph* graph) {
562 Definition* current_defn = current->AsDefinition();
563 if ((replacement != NULL) && (current_defn != NULL)) {
564 Definition* replacement_defn = replacement->AsDefinition();
565 ASSERT(replacement_defn != NULL);
566 current_defn->ReplaceUsesWith(replacement_defn);
567 EnsureSSATempIndex(graph, current_defn, replacement_defn);
568
569 if (FLAG_trace_optimization) {
570 OS::Print("Replacing v%" Pd " with v%" Pd "\n",
571 current_defn->ssa_temp_index(),
572 replacement_defn->ssa_temp_index());
573 }
574 } else if (FLAG_trace_optimization) {
575 if (current_defn == NULL) {
576 OS::Print("Removing %s\n", current->DebugName());
577 } else {
578 ASSERT(!current_defn->HasUses());
579 OS::Print("Removing v%" Pd ".\n", current_defn->ssa_temp_index());
580 }
581 }
582 iterator->RemoveCurrentFromGraph();
583 }
584
585
586 bool FlowGraphOptimizer::Canonicalize() {
587 bool changed = false;
588 for (intptr_t i = 0; i < block_order_.length(); ++i) {
589 BlockEntryInstr* entry = block_order_[i];
590 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
591 Instruction* current = it.Current();
592 if (current->HasUnmatchedInputRepresentations()) {
593 // Can't canonicalize this instruction until all conversions for its
594 // inputs are inserted.
595 continue;
596 }
597
598 Instruction* replacement = current->Canonicalize(flow_graph());
599
600 if (replacement != current) {
601 // For non-definitions Canonicalize should return either NULL or
602 // this.
603 ASSERT((replacement == NULL) || current->IsDefinition());
604 ReplaceCurrentInstruction(&it, current, replacement, flow_graph_);
605 changed = true;
606 }
607 }
608 }
609 return changed;
610 }
611
612
613 static bool IsUnboxedInteger(Representation rep) {
614 return (rep == kUnboxedInt32) ||
615 (rep == kUnboxedUint32) ||
616 (rep == kUnboxedMint);
617 }
618
619
620 void FlowGraphOptimizer::InsertConversion(Representation from,
621 Representation to,
622 Value* use,
623 bool is_environment_use) {
624 Instruction* insert_before;
625 Instruction* deopt_target;
626 PhiInstr* phi = use->instruction()->AsPhi();
627 if (phi != NULL) {
628 ASSERT(phi->is_alive());
629 // For phis conversions have to be inserted in the predecessor.
630 insert_before =
631 phi->block()->PredecessorAt(use->use_index())->last_instruction();
632 deopt_target = NULL;
633 } else {
634 deopt_target = insert_before = use->instruction();
635 }
636
637 Definition* converted = NULL;
638 if (IsUnboxedInteger(from) && IsUnboxedInteger(to)) {
639 const intptr_t deopt_id = (to == kUnboxedInt32) && (deopt_target != NULL) ?
640 deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
641 converted = new(I) UnboxedIntConverterInstr(from,
642 to,
643 use->CopyWithType(),
644 deopt_id);
645 } else if ((from == kUnboxedInt32) && (to == kUnboxedDouble)) {
646 converted = new Int32ToDoubleInstr(use->CopyWithType());
647 } else if ((from == kUnboxedMint) &&
648 (to == kUnboxedDouble) &&
649 CanConvertUnboxedMintToDouble()) {
650 const intptr_t deopt_id = (deopt_target != NULL) ?
651 deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
652 ASSERT(CanUnboxDouble());
653 converted = new MintToDoubleInstr(use->CopyWithType(), deopt_id);
654 } else if ((from == kTagged) && Boxing::Supports(to)) {
655 const intptr_t deopt_id = (deopt_target != NULL) ?
656 deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
657 converted = UnboxInstr::Create(to, use->CopyWithType(), deopt_id);
658 } else if ((to == kTagged) && Boxing::Supports(from)) {
659 converted = BoxInstr::Create(from, use->CopyWithType());
660 } else {
661 // We have failed to find a suitable conversion instruction.
662 // Insert two "dummy" conversion instructions with the correct
663 // "from" and "to" representation. The inserted instructions will
664 // trigger a deoptimization if executed. See #12417 for a discussion.
665 const intptr_t deopt_id = (deopt_target != NULL) ?
666 deopt_target->DeoptimizationTarget() : Isolate::kNoDeoptId;
667 ASSERT(Boxing::Supports(from));
668 ASSERT(Boxing::Supports(to));
669 Definition* boxed = BoxInstr::Create(from, use->CopyWithType());
670 use->BindTo(boxed);
671 InsertBefore(insert_before, boxed, NULL, FlowGraph::kValue);
672 converted = UnboxInstr::Create(to, new(I) Value(boxed), deopt_id);
673 }
674 ASSERT(converted != NULL);
675 InsertBefore(insert_before, converted, use->instruction()->env(),
676 FlowGraph::kValue);
677 if (is_environment_use) {
678 use->BindToEnvironment(converted);
679 } else {
680 use->BindTo(converted);
681 }
682
683 if ((to == kUnboxedInt32) && (phi != NULL)) {
684 // Int32 phis are unboxed optimistically. Ensure that unboxing
685 // has deoptimization target attached from the goto instruction.
686 flow_graph_->CopyDeoptTarget(converted, insert_before);
687 }
688 }
689
690
691 void FlowGraphOptimizer::ConvertUse(Value* use, Representation from_rep) {
692 const Representation to_rep =
693 use->instruction()->RequiredInputRepresentation(use->use_index());
694 if (from_rep == to_rep || to_rep == kNoRepresentation) {
695 return;
696 }
697 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ false);
698 }
699
700
701 void FlowGraphOptimizer::ConvertEnvironmentUse(Value* use,
702 Representation from_rep) {
703 const Representation to_rep = kTagged;
704 if (from_rep == to_rep || to_rep == kNoRepresentation) {
705 return;
706 }
707 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ true);
708 }
709
710
711 void FlowGraphOptimizer::InsertConversionsFor(Definition* def) {
712 const Representation from_rep = def->representation();
713
714 for (Value::Iterator it(def->input_use_list());
715 !it.Done();
716 it.Advance()) {
717 ConvertUse(it.Current(), from_rep);
718 }
719
720 if (flow_graph()->graph_entry()->SuccessorCount() > 1) {
721 for (Value::Iterator it(def->env_use_list());
722 !it.Done();
723 it.Advance()) {
724 Value* use = it.Current();
725 if (use->instruction()->MayThrow() &&
726 use->instruction()->GetBlock()->InsideTryBlock()) {
727 // Environment uses at calls inside try-blocks must be converted to
728 // tagged representation.
729 ConvertEnvironmentUse(it.Current(), from_rep);
730 }
731 }
732 }
733 }
734
735
736 static void UnboxPhi(PhiInstr* phi) {
737 Representation unboxed = phi->representation();
738
739 switch (phi->Type()->ToCid()) {
740 case kDoubleCid:
741 if (CanUnboxDouble()) {
742 unboxed = kUnboxedDouble;
743 }
744 break;
745 case kFloat32x4Cid:
746 if (ShouldInlineSimd()) {
747 unboxed = kUnboxedFloat32x4;
748 }
749 break;
750 case kInt32x4Cid:
751 if (ShouldInlineSimd()) {
752 unboxed = kUnboxedInt32x4;
753 }
754 break;
755 case kFloat64x2Cid:
756 if (ShouldInlineSimd()) {
757 unboxed = kUnboxedFloat64x2;
758 }
759 break;
760 }
761
762 if ((kSmiBits < 32) &&
763 (unboxed == kTagged) &&
764 phi->Type()->IsInt() &&
765 RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32)) {
766 // On 32-bit platforms conservatively unbox phis that:
767 // - are proven to be of type Int;
768 // - fit into 32bits range;
769 // - have either constants or Box() operations as inputs;
770 // - have at least one Box() operation as an input;
771 // - are used in at least 1 Unbox() operation.
772 bool should_unbox = false;
773 for (intptr_t i = 0; i < phi->InputCount(); i++) {
774 Definition* input = phi->InputAt(i)->definition();
775 if (input->IsBox() &&
776 RangeUtils::Fits(input->range(),
777 RangeBoundary::kRangeBoundaryInt32)) {
778 should_unbox = true;
779 } else if (!input->IsConstant()) {
780 should_unbox = false;
781 break;
782 }
783 }
784
785 if (should_unbox) {
786 // We checked inputs. Check if phi is used in at least one unbox
787 // operation.
788 bool has_unboxed_use = false;
789 for (Value* use = phi->input_use_list();
790 use != NULL;
791 use = use->next_use()) {
792 Instruction* instr = use->instruction();
793 if (instr->IsUnbox()) {
794 has_unboxed_use = true;
795 break;
796 } else if (IsUnboxedInteger(
797 instr->RequiredInputRepresentation(use->use_index()))) {
798 has_unboxed_use = true;
799 break;
800 }
801 }
802
803 if (!has_unboxed_use) {
804 should_unbox = false;
805 }
806 }
807
808 if (should_unbox) {
809 unboxed = kUnboxedInt32;
810 }
811 }
812
813 phi->set_representation(unboxed);
814 }
815
816
817 void FlowGraphOptimizer::SelectRepresentations() {
818 // Conservatively unbox all phis that were proven to be of Double,
819 // Float32x4, or Int32x4 type.
820 for (intptr_t i = 0; i < block_order_.length(); ++i) {
821 JoinEntryInstr* join_entry = block_order_[i]->AsJoinEntry();
822 if (join_entry != NULL) {
823 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
824 PhiInstr* phi = it.Current();
825 UnboxPhi(phi);
826 }
827 }
828 }
829
830 // Process all instructions and insert conversions where needed.
831 GraphEntryInstr* graph_entry = block_order_[0]->AsGraphEntry();
832
833 // Visit incoming parameters and constants.
834 for (intptr_t i = 0; i < graph_entry->initial_definitions()->length(); i++) {
835 InsertConversionsFor((*graph_entry->initial_definitions())[i]);
836 }
837
838 for (intptr_t i = 0; i < block_order_.length(); ++i) {
839 BlockEntryInstr* entry = block_order_[i];
840 JoinEntryInstr* join_entry = entry->AsJoinEntry();
841 if (join_entry != NULL) {
842 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
843 PhiInstr* phi = it.Current();
844 ASSERT(phi != NULL);
845 ASSERT(phi->is_alive());
846 InsertConversionsFor(phi);
847 }
848 }
849 CatchBlockEntryInstr* catch_entry = entry->AsCatchBlockEntry();
850 if (catch_entry != NULL) {
851 for (intptr_t i = 0;
852 i < catch_entry->initial_definitions()->length();
853 i++) {
854 InsertConversionsFor((*catch_entry->initial_definitions())[i]);
855 }
856 }
857 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
858 Definition* def = it.Current()->AsDefinition();
859 if (def != NULL) {
860 InsertConversionsFor(def);
861 }
862 }
863 }
864 }
865
866
867 static bool ClassIdIsOneOf(intptr_t class_id,
868 const GrowableArray<intptr_t>& class_ids) {
869 for (intptr_t i = 0; i < class_ids.length(); i++) {
870 ASSERT(class_ids[i] != kIllegalCid);
871 if (class_ids[i] == class_id) {
872 return true;
873 }
874 }
875 return false;
876 }
877
878
879 // Returns true if ICData tests two arguments and all ICData cids are in the
880 // required sets 'receiver_class_ids' or 'argument_class_ids', respectively.
881 static bool ICDataHasOnlyReceiverArgumentClassIds(
882 const ICData& ic_data,
883 const GrowableArray<intptr_t>& receiver_class_ids,
884 const GrowableArray<intptr_t>& argument_class_ids) {
885 if (ic_data.NumArgsTested() != 2) {
886 return false;
887 }
888 Function& target = Function::Handle();
889 const intptr_t len = ic_data.NumberOfChecks();
890 GrowableArray<intptr_t> class_ids;
891 for (intptr_t i = 0; i < len; i++) {
892 if (ic_data.IsUsedAt(i)) {
893 ic_data.GetCheckAt(i, &class_ids, &target);
894 ASSERT(class_ids.length() == 2);
895 if (!ClassIdIsOneOf(class_ids[0], receiver_class_ids) ||
896 !ClassIdIsOneOf(class_ids[1], argument_class_ids)) {
897 return false;
898 }
899 }
900 }
901 return true;
902 }
903
904
905 static bool ICDataHasReceiverArgumentClassIds(const ICData& ic_data,
906 intptr_t receiver_class_id,
907 intptr_t argument_class_id) {
908 if (ic_data.NumArgsTested() != 2) {
909 return false;
910 }
911 Function& target = Function::Handle();
912 const intptr_t len = ic_data.NumberOfChecks();
913 for (intptr_t i = 0; i < len; i++) {
914 if (ic_data.IsUsedAt(i)) {
915 GrowableArray<intptr_t> class_ids;
916 ic_data.GetCheckAt(i, &class_ids, &target);
917 ASSERT(class_ids.length() == 2);
918 if ((class_ids[0] == receiver_class_id) &&
919 (class_ids[1] == argument_class_id)) {
920 return true;
921 }
922 }
923 }
924 return false;
925 }
926
927
928 static bool HasOnlyOneSmi(const ICData& ic_data) {
929 return (ic_data.NumberOfUsedChecks() == 1)
930 && ic_data.HasReceiverClassId(kSmiCid);
931 }
932
933
934 static bool HasOnlySmiOrMint(const ICData& ic_data) {
935 if (ic_data.NumberOfUsedChecks() == 1) {
936 return ic_data.HasReceiverClassId(kSmiCid)
937 || ic_data.HasReceiverClassId(kMintCid);
938 }
939 return (ic_data.NumberOfUsedChecks() == 2)
940 && ic_data.HasReceiverClassId(kSmiCid)
941 && ic_data.HasReceiverClassId(kMintCid);
942 }
943
944
945 static bool HasOnlyTwoOf(const ICData& ic_data, intptr_t cid) {
946 if (ic_data.NumberOfUsedChecks() != 1) {
947 return false;
948 }
949 GrowableArray<intptr_t> first;
950 GrowableArray<intptr_t> second;
951 ic_data.GetUsedCidsForTwoArgs(&first, &second);
952 return (first[0] == cid) && (second[0] == cid);
953 }
954
955 // Returns false if the ICData contains anything other than the 4 combinations
956 // of Mint and Smi for the receiver and argument classes.
957 static bool HasTwoMintOrSmi(const ICData& ic_data) {
958 GrowableArray<intptr_t> first;
959 GrowableArray<intptr_t> second;
960 ic_data.GetUsedCidsForTwoArgs(&first, &second);
961 for (intptr_t i = 0; i < first.length(); i++) {
962 if ((first[i] != kSmiCid) && (first[i] != kMintCid)) {
963 return false;
964 }
965 if ((second[i] != kSmiCid) && (second[i] != kMintCid)) {
966 return false;
967 }
968 }
969 return true;
970 }
971
972
973 // Returns false if the ICData contains anything other than the 4 combinations
974 // of Double and Smi for the receiver and argument classes.
975 static bool HasTwoDoubleOrSmi(const ICData& ic_data) {
976 GrowableArray<intptr_t> class_ids(2);
977 class_ids.Add(kSmiCid);
978 class_ids.Add(kDoubleCid);
979 return ICDataHasOnlyReceiverArgumentClassIds(ic_data, class_ids, class_ids);
980 }
981
982
983 static bool HasOnlyOneDouble(const ICData& ic_data) {
984 return (ic_data.NumberOfUsedChecks() == 1)
985 && ic_data.HasReceiverClassId(kDoubleCid);
986 }
987
988
989 static bool ShouldSpecializeForDouble(const ICData& ic_data) {
990 // Don't specialize for double if we can't unbox them.
991 if (!CanUnboxDouble()) {
992 return false;
993 }
994
995 // Unboxed double operation can't handle case of two smis.
996 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
997 return false;
998 }
999
1000 // Check that it have seen only smis and doubles.
1001 return HasTwoDoubleOrSmi(ic_data);
1002 }
1003
1004
1005 void FlowGraphOptimizer::ReplaceCall(Definition* call,
1006 Definition* replacement) {
1007 // Remove the original push arguments.
1008 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
1009 PushArgumentInstr* push = call->PushArgumentAt(i);
1010 push->ReplaceUsesWith(push->value()->definition());
1011 push->RemoveFromGraph();
1012 }
1013 call->ReplaceWith(replacement, current_iterator());
1014 }
1015
1016
1017 void FlowGraphOptimizer::AddCheckSmi(Definition* to_check,
1018 intptr_t deopt_id,
1019 Environment* deopt_environment,
1020 Instruction* insert_before) {
1021 if (to_check->Type()->ToCid() != kSmiCid) {
1022 InsertBefore(insert_before,
1023 new(I) CheckSmiInstr(new(I) Value(to_check),
1024 deopt_id,
1025 insert_before->token_pos()),
1026 deopt_environment,
1027 FlowGraph::kEffect);
1028 }
1029 }
1030
1031
1032 Instruction* FlowGraphOptimizer::GetCheckClass(Definition* to_check,
1033 const ICData& unary_checks,
1034 intptr_t deopt_id,
1035 intptr_t token_pos) {
1036 if ((unary_checks.NumberOfUsedChecks() == 1) &&
1037 unary_checks.HasReceiverClassId(kSmiCid)) {
1038 return new(I) CheckSmiInstr(new(I) Value(to_check),
1039 deopt_id,
1040 token_pos);
1041 }
1042 return new(I) CheckClassInstr(
1043 new(I) Value(to_check), deopt_id, unary_checks, token_pos);
1044 }
1045
1046
1047 void FlowGraphOptimizer::AddCheckClass(Definition* to_check,
1048 const ICData& unary_checks,
1049 intptr_t deopt_id,
1050 Environment* deopt_environment,
1051 Instruction* insert_before) {
1052 // Type propagation has not run yet, we cannot eliminate the check.
1053 Instruction* check = GetCheckClass(
1054 to_check, unary_checks, deopt_id, insert_before->token_pos());
1055 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect);
1056 }
1057
1058
1059 void FlowGraphOptimizer::AddReceiverCheck(InstanceCallInstr* call) {
1060 AddCheckClass(call->ArgumentAt(0),
1061 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecks()),
1062 call->deopt_id(),
1063 call->env(),
1064 call);
1065 }
1066
1067
1068 static bool ArgIsAlways(intptr_t cid,
1069 const ICData& ic_data,
1070 intptr_t arg_number) {
1071 ASSERT(ic_data.NumArgsTested() > arg_number);
1072 if (ic_data.NumberOfUsedChecks() == 0) {
1073 return false;
1074 }
1075 const intptr_t num_checks = ic_data.NumberOfChecks();
1076 for (intptr_t i = 0; i < num_checks; i++) {
1077 if (ic_data.IsUsedAt(i) && ic_data.GetClassIdAt(i, arg_number) != cid) {
1078 return false;
1079 }
1080 }
1081 return true;
1082 }
1083
1084
1085 static bool CanUnboxInt32() {
1086 // Int32/Uint32 can be unboxed if it fits into a smi or the platform
1087 // supports unboxed mints.
1088 return (kSmiBits >= 32) || FlowGraphCompiler::SupportsUnboxedMints();
1089 }
1090
1091
1092 static intptr_t MethodKindToCid(MethodRecognizer::Kind kind) {
1093 switch (kind) {
1094 case MethodRecognizer::kImmutableArrayGetIndexed:
1095 return kImmutableArrayCid;
1096
1097 case MethodRecognizer::kObjectArrayGetIndexed:
1098 case MethodRecognizer::kObjectArraySetIndexed:
1099 return kArrayCid;
1100
1101 case MethodRecognizer::kGrowableArrayGetIndexed:
1102 case MethodRecognizer::kGrowableArraySetIndexed:
1103 return kGrowableObjectArrayCid;
1104
1105 case MethodRecognizer::kFloat32ArrayGetIndexed:
1106 case MethodRecognizer::kFloat32ArraySetIndexed:
1107 return kTypedDataFloat32ArrayCid;
1108
1109 case MethodRecognizer::kFloat64ArrayGetIndexed:
1110 case MethodRecognizer::kFloat64ArraySetIndexed:
1111 return kTypedDataFloat64ArrayCid;
1112
1113 case MethodRecognizer::kInt8ArrayGetIndexed:
1114 case MethodRecognizer::kInt8ArraySetIndexed:
1115 return kTypedDataInt8ArrayCid;
1116
1117 case MethodRecognizer::kUint8ArrayGetIndexed:
1118 case MethodRecognizer::kUint8ArraySetIndexed:
1119 return kTypedDataUint8ArrayCid;
1120
1121 case MethodRecognizer::kUint8ClampedArrayGetIndexed:
1122 case MethodRecognizer::kUint8ClampedArraySetIndexed:
1123 return kTypedDataUint8ClampedArrayCid;
1124
1125 case MethodRecognizer::kExternalUint8ArrayGetIndexed:
1126 case MethodRecognizer::kExternalUint8ArraySetIndexed:
1127 return kExternalTypedDataUint8ArrayCid;
1128
1129 case MethodRecognizer::kExternalUint8ClampedArrayGetIndexed:
1130 case MethodRecognizer::kExternalUint8ClampedArraySetIndexed:
1131 return kExternalTypedDataUint8ClampedArrayCid;
1132
1133 case MethodRecognizer::kInt16ArrayGetIndexed:
1134 case MethodRecognizer::kInt16ArraySetIndexed:
1135 return kTypedDataInt16ArrayCid;
1136
1137 case MethodRecognizer::kUint16ArrayGetIndexed:
1138 case MethodRecognizer::kUint16ArraySetIndexed:
1139 return kTypedDataUint16ArrayCid;
1140
1141 case MethodRecognizer::kInt32ArrayGetIndexed:
1142 case MethodRecognizer::kInt32ArraySetIndexed:
1143 return kTypedDataInt32ArrayCid;
1144
1145 case MethodRecognizer::kUint32ArrayGetIndexed:
1146 case MethodRecognizer::kUint32ArraySetIndexed:
1147 return kTypedDataUint32ArrayCid;
1148
1149 case MethodRecognizer::kInt64ArrayGetIndexed:
1150 case MethodRecognizer::kInt64ArraySetIndexed:
1151 return kTypedDataInt64ArrayCid;
1152
1153 case MethodRecognizer::kFloat32x4ArrayGetIndexed:
1154 case MethodRecognizer::kFloat32x4ArraySetIndexed:
1155 return kTypedDataFloat32x4ArrayCid;
1156
1157 case MethodRecognizer::kInt32x4ArrayGetIndexed:
1158 case MethodRecognizer::kInt32x4ArraySetIndexed:
1159 return kTypedDataInt32x4ArrayCid;
1160
1161 case MethodRecognizer::kFloat64x2ArrayGetIndexed:
1162 case MethodRecognizer::kFloat64x2ArraySetIndexed:
1163 return kTypedDataFloat64x2ArrayCid;
1164
1165 default:
1166 break;
1167 }
1168 return kIllegalCid;
1169 }
1170
1171
1172 bool FlowGraphOptimizer::TryReplaceWithStoreIndexed(InstanceCallInstr* call) {
1173 // Check for monomorphic IC data.
1174 if (!call->HasICData()) return false;
1175 const ICData& ic_data =
1176 ICData::Handle(I, call->ic_data()->AsUnaryClassChecks());
1177 if (ic_data.NumberOfChecks() != 1) {
1178 return false;
1179 }
1180 ASSERT(ic_data.NumberOfUsedChecks() == 1);
1181 ASSERT(ic_data.HasOneTarget());
1182
1183 const Function& target = Function::Handle(I, ic_data.GetTargetAt(0));
1184 TargetEntryInstr* entry;
1185 Definition* last;
1186 if (!TryInlineRecognizedMethod(ic_data.GetReceiverClassIdAt(0),
1187 target,
1188 call,
1189 call->ArgumentAt(0),
1190 call->token_pos(),
1191 *call->ic_data(),
1192 &entry, &last)) {
1193 return false;
1194 }
1195 // Insert receiver class check.
1196 AddReceiverCheck(call);
1197 // Remove the original push arguments.
1198 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
1199 PushArgumentInstr* push = call->PushArgumentAt(i);
1200 push->ReplaceUsesWith(push->value()->definition());
1201 push->RemoveFromGraph();
1202 }
1203 // Replace all uses of this definition with the result.
1204 call->ReplaceUsesWith(last);
1205 // Finally insert the sequence other definition in place of this one in the
1206 // graph.
1207 call->previous()->LinkTo(entry->next());
1208 entry->UnuseAllInputs(); // Entry block is not in the graph.
1209 last->LinkTo(call);
1210 // Remove through the iterator.
1211 ASSERT(current_iterator()->Current() == call);
1212 current_iterator()->RemoveCurrentFromGraph();
1213 call->set_previous(NULL);
1214 call->set_next(NULL);
1215 return true;
1216 }
1217
1218
1219 bool FlowGraphOptimizer::InlineSetIndexed(
1220 MethodRecognizer::Kind kind,
1221 const Function& target,
1222 Instruction* call,
1223 Definition* receiver,
1224 intptr_t token_pos,
1225 const ICData* ic_data,
1226 const ICData& value_check,
1227 TargetEntryInstr** entry,
1228 Definition** last) {
1229 intptr_t array_cid = MethodKindToCid(kind);
1230 ASSERT(array_cid != kIllegalCid);
1231
1232 Definition* array = receiver;
1233 Definition* index = call->ArgumentAt(1);
1234 Definition* stored_value = call->ArgumentAt(2);
1235
1236 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
1237 call->GetBlock()->try_index());
1238 (*entry)->InheritDeoptTarget(I, call);
1239 Instruction* cursor = *entry;
1240 if (FLAG_enable_type_checks) {
1241 // Only type check for the value. A type check for the index is not
1242 // needed here because we insert a deoptimizing smi-check for the case
1243 // the index is not a smi.
1244 const AbstractType& value_type =
1245 AbstractType::ZoneHandle(I, target.ParameterTypeAt(2));
1246 Definition* instantiator = NULL;
1247 Definition* type_args = NULL;
1248 switch (array_cid) {
1249 case kArrayCid:
1250 case kGrowableObjectArrayCid: {
1251 const Class& instantiator_class = Class::Handle(I, target.Owner());
1252 intptr_t type_arguments_field_offset =
1253 instantiator_class.type_arguments_field_offset();
1254 LoadFieldInstr* load_type_args =
1255 new(I) LoadFieldInstr(new(I) Value(array),
1256 type_arguments_field_offset,
1257 Type::ZoneHandle(I), // No type.
1258 call->token_pos());
1259 cursor = flow_graph()->AppendTo(cursor,
1260 load_type_args,
1261 NULL,
1262 FlowGraph::kValue);
1263
1264 instantiator = array;
1265 type_args = load_type_args;
1266 break;
1267 }
1268 case kTypedDataInt8ArrayCid:
1269 case kTypedDataUint8ArrayCid:
1270 case kTypedDataUint8ClampedArrayCid:
1271 case kExternalTypedDataUint8ArrayCid:
1272 case kExternalTypedDataUint8ClampedArrayCid:
1273 case kTypedDataInt16ArrayCid:
1274 case kTypedDataUint16ArrayCid:
1275 case kTypedDataInt32ArrayCid:
1276 case kTypedDataUint32ArrayCid:
1277 case kTypedDataInt64ArrayCid:
1278 ASSERT(value_type.IsIntType());
1279 // Fall through.
1280 case kTypedDataFloat32ArrayCid:
1281 case kTypedDataFloat64ArrayCid: {
1282 type_args = instantiator = flow_graph_->constant_null();
1283 ASSERT((array_cid != kTypedDataFloat32ArrayCid &&
1284 array_cid != kTypedDataFloat64ArrayCid) ||
1285 value_type.IsDoubleType());
1286 ASSERT(value_type.IsInstantiated());
1287 break;
1288 }
1289 case kTypedDataFloat32x4ArrayCid: {
1290 type_args = instantiator = flow_graph_->constant_null();
1291 ASSERT((array_cid != kTypedDataFloat32x4ArrayCid) ||
1292 value_type.IsFloat32x4Type());
1293 ASSERT(value_type.IsInstantiated());
1294 break;
1295 }
1296 case kTypedDataFloat64x2ArrayCid: {
1297 type_args = instantiator = flow_graph_->constant_null();
1298 ASSERT((array_cid != kTypedDataFloat64x2ArrayCid) ||
1299 value_type.IsFloat64x2Type());
1300 ASSERT(value_type.IsInstantiated());
1301 break;
1302 }
1303 default:
1304 // TODO(fschneider): Add support for other array types.
1305 UNREACHABLE();
1306 }
1307 AssertAssignableInstr* assert_value =
1308 new(I) AssertAssignableInstr(token_pos,
1309 new(I) Value(stored_value),
1310 new(I) Value(instantiator),
1311 new(I) Value(type_args),
1312 value_type,
1313 Symbols::Value(),
1314 call->deopt_id());
1315 cursor = flow_graph()->AppendTo(cursor,
1316 assert_value,
1317 call->env(),
1318 FlowGraph::kValue);
1319 }
1320
1321 array_cid = PrepareInlineIndexedOp(call,
1322 array_cid,
1323 &array,
1324 index,
1325 &cursor);
1326
1327 // Check if store barrier is needed. Byte arrays don't need a store barrier.
1328 StoreBarrierType needs_store_barrier =
1329 (RawObject::IsTypedDataClassId(array_cid) ||
1330 RawObject::IsTypedDataViewClassId(array_cid) ||
1331 RawObject::IsExternalTypedDataClassId(array_cid)) ? kNoStoreBarrier
1332 : kEmitStoreBarrier;
1333
1334 // No need to class check stores to Int32 and Uint32 arrays because
1335 // we insert unboxing instructions below which include a class check.
1336 if ((array_cid != kTypedDataUint32ArrayCid) &&
1337 (array_cid != kTypedDataInt32ArrayCid) &&
1338 !value_check.IsNull()) {
1339 // No store barrier needed because checked value is a smi, an unboxed mint,
1340 // an unboxed double, an unboxed Float32x4, or unboxed Int32x4.
1341 needs_store_barrier = kNoStoreBarrier;
1342 Instruction* check = GetCheckClass(
1343 stored_value, value_check, call->deopt_id(), call->token_pos());
1344 cursor = flow_graph()->AppendTo(cursor,
1345 check,
1346 call->env(),
1347 FlowGraph::kEffect);
1348 }
1349
1350 if (array_cid == kTypedDataFloat32ArrayCid) {
1351 stored_value =
1352 new(I) DoubleToFloatInstr(
1353 new(I) Value(stored_value), call->deopt_id());
1354 cursor = flow_graph()->AppendTo(cursor,
1355 stored_value,
1356 NULL,
1357 FlowGraph::kValue);
1358 } else if (array_cid == kTypedDataInt32ArrayCid) {
1359 stored_value = new(I) UnboxInt32Instr(
1360 UnboxInt32Instr::kTruncate,
1361 new(I) Value(stored_value),
1362 call->deopt_id());
1363 cursor = flow_graph()->AppendTo(cursor,
1364 stored_value,
1365 call->env(),
1366 FlowGraph::kValue);
1367 } else if (array_cid == kTypedDataUint32ArrayCid) {
1368 stored_value = new(I) UnboxUint32Instr(
1369 new(I) Value(stored_value),
1370 call->deopt_id());
1371 ASSERT(stored_value->AsUnboxInteger()->is_truncating());
1372 cursor = flow_graph()->AppendTo(cursor,
1373 stored_value,
1374 call->env(),
1375 FlowGraph::kValue);
1376 }
1377
1378 const intptr_t index_scale = Instance::ElementSizeFor(array_cid);
1379 *last = new(I) StoreIndexedInstr(new(I) Value(array),
1380 new(I) Value(index),
1381 new(I) Value(stored_value),
1382 needs_store_barrier,
1383 index_scale,
1384 array_cid,
1385 call->deopt_id(),
1386 call->token_pos());
1387 flow_graph()->AppendTo(cursor,
1388 *last,
1389 call->env(),
1390 FlowGraph::kEffect);
1391 return true;
1392 }
1393
1394
1395 bool FlowGraphOptimizer::TryInlineRecognizedMethod(intptr_t receiver_cid,
1396 const Function& target,
1397 Instruction* call,
1398 Definition* receiver,
1399 intptr_t token_pos,
1400 const ICData& ic_data,
1401 TargetEntryInstr** entry,
1402 Definition** last) {
1403 ICData& value_check = ICData::ZoneHandle(I);
1404 MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(target);
1405 switch (kind) {
1406 // Recognized [] operators.
1407 case MethodRecognizer::kImmutableArrayGetIndexed:
1408 case MethodRecognizer::kObjectArrayGetIndexed:
1409 case MethodRecognizer::kGrowableArrayGetIndexed:
1410 case MethodRecognizer::kInt8ArrayGetIndexed:
1411 case MethodRecognizer::kUint8ArrayGetIndexed:
1412 case MethodRecognizer::kUint8ClampedArrayGetIndexed:
1413 case MethodRecognizer::kExternalUint8ArrayGetIndexed:
1414 case MethodRecognizer::kExternalUint8ClampedArrayGetIndexed:
1415 case MethodRecognizer::kInt16ArrayGetIndexed:
1416 case MethodRecognizer::kUint16ArrayGetIndexed:
1417 return InlineGetIndexed(kind, call, receiver, ic_data, entry, last);
1418 case MethodRecognizer::kFloat32ArrayGetIndexed:
1419 case MethodRecognizer::kFloat64ArrayGetIndexed:
1420 if (!CanUnboxDouble()) {
1421 return false;
1422 }
1423 return InlineGetIndexed(kind, call, receiver, ic_data, entry, last);
1424 case MethodRecognizer::kFloat32x4ArrayGetIndexed:
1425 case MethodRecognizer::kFloat64x2ArrayGetIndexed:
1426 if (!ShouldInlineSimd()) {
1427 return false;
1428 }
1429 return InlineGetIndexed(kind, call, receiver, ic_data, entry, last);
1430 case MethodRecognizer::kInt32ArrayGetIndexed:
1431 case MethodRecognizer::kUint32ArrayGetIndexed:
1432 if (!CanUnboxInt32()) return false;
1433 return InlineGetIndexed(kind, call, receiver, ic_data, entry, last);
1434
1435 case MethodRecognizer::kInt64ArrayGetIndexed:
1436 if (!ShouldInlineInt64ArrayOps()) {
1437 return false;
1438 }
1439 return InlineGetIndexed(kind, call, receiver, ic_data, entry, last);
1440 // Recognized []= operators.
1441 case MethodRecognizer::kObjectArraySetIndexed:
1442 case MethodRecognizer::kGrowableArraySetIndexed:
1443 if (ArgIsAlways(kSmiCid, ic_data, 2)) {
1444 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1445 }
1446 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1447 &ic_data, value_check, entry, last);
1448 case MethodRecognizer::kInt8ArraySetIndexed:
1449 case MethodRecognizer::kUint8ArraySetIndexed:
1450 case MethodRecognizer::kUint8ClampedArraySetIndexed:
1451 case MethodRecognizer::kExternalUint8ArraySetIndexed:
1452 case MethodRecognizer::kExternalUint8ClampedArraySetIndexed:
1453 case MethodRecognizer::kInt16ArraySetIndexed:
1454 case MethodRecognizer::kUint16ArraySetIndexed:
1455 if (!ArgIsAlways(kSmiCid, ic_data, 2)) {
1456 return false;
1457 }
1458 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1459 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1460 &ic_data, value_check, entry, last);
1461 case MethodRecognizer::kInt32ArraySetIndexed:
1462 case MethodRecognizer::kUint32ArraySetIndexed:
1463 // Check that value is always smi or mint. We use Int32/Uint32 unboxing
1464 // which can only deal unbox these values.
1465 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1466 if (!HasOnlySmiOrMint(value_check)) {
1467 return false;
1468 }
1469 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1470 &ic_data, value_check, entry, last);
1471 case MethodRecognizer::kInt64ArraySetIndexed:
1472 if (!ShouldInlineInt64ArrayOps()) {
1473 return false;
1474 }
1475 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1476 &ic_data, value_check, entry, last);
1477 case MethodRecognizer::kFloat32ArraySetIndexed:
1478 case MethodRecognizer::kFloat64ArraySetIndexed:
1479 if (!CanUnboxDouble()) {
1480 return false;
1481 }
1482 // Check that value is always double.
1483 if (!ArgIsAlways(kDoubleCid, ic_data, 2)) {
1484 return false;
1485 }
1486 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1487 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1488 &ic_data, value_check, entry, last);
1489 case MethodRecognizer::kFloat32x4ArraySetIndexed:
1490 if (!ShouldInlineSimd()) {
1491 return false;
1492 }
1493 // Check that value is always a Float32x4.
1494 if (!ArgIsAlways(kFloat32x4Cid, ic_data, 2)) {
1495 return false;
1496 }
1497 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1498 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1499 &ic_data, value_check, entry, last);
1500 case MethodRecognizer::kFloat64x2ArraySetIndexed:
1501 if (!ShouldInlineSimd()) {
1502 return false;
1503 }
1504 // Check that value is always a Float32x4.
1505 if (!ArgIsAlways(kFloat64x2Cid, ic_data, 2)) {
1506 return false;
1507 }
1508 value_check = ic_data.AsUnaryClassChecksForArgNr(2);
1509 return InlineSetIndexed(kind, target, call, receiver, token_pos,
1510 &ic_data, value_check, entry, last);
1511 case MethodRecognizer::kByteArrayBaseGetInt8:
1512 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1513 kTypedDataInt8ArrayCid,
1514 ic_data, entry, last);
1515 case MethodRecognizer::kByteArrayBaseGetUint8:
1516 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1517 kTypedDataUint8ArrayCid,
1518 ic_data, entry, last);
1519 case MethodRecognizer::kByteArrayBaseGetInt16:
1520 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1521 kTypedDataInt16ArrayCid,
1522 ic_data, entry, last);
1523 case MethodRecognizer::kByteArrayBaseGetUint16:
1524 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1525 kTypedDataUint16ArrayCid,
1526 ic_data, entry, last);
1527 case MethodRecognizer::kByteArrayBaseGetInt32:
1528 if (!CanUnboxInt32()) {
1529 return false;
1530 }
1531 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1532 kTypedDataInt32ArrayCid,
1533 ic_data, entry, last);
1534 case MethodRecognizer::kByteArrayBaseGetUint32:
1535 if (!CanUnboxInt32()) {
1536 return false;
1537 }
1538 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1539 kTypedDataUint32ArrayCid,
1540 ic_data, entry, last);
1541 case MethodRecognizer::kByteArrayBaseGetFloat32:
1542 if (!CanUnboxDouble()) {
1543 return false;
1544 }
1545 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1546 kTypedDataFloat32ArrayCid,
1547 ic_data, entry, last);
1548 case MethodRecognizer::kByteArrayBaseGetFloat64:
1549 if (!CanUnboxDouble()) {
1550 return false;
1551 }
1552 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1553 kTypedDataFloat64ArrayCid,
1554 ic_data, entry, last);
1555 case MethodRecognizer::kByteArrayBaseGetFloat32x4:
1556 if (!ShouldInlineSimd()) {
1557 return false;
1558 }
1559 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1560 kTypedDataFloat32x4ArrayCid,
1561 ic_data, entry, last);
1562 case MethodRecognizer::kByteArrayBaseGetInt32x4:
1563 if (!ShouldInlineSimd()) {
1564 return false;
1565 }
1566 return InlineByteArrayViewLoad(call, receiver, receiver_cid,
1567 kTypedDataInt32x4ArrayCid,
1568 ic_data, entry, last);
1569 case MethodRecognizer::kByteArrayBaseSetInt8:
1570 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1571 kTypedDataInt8ArrayCid,
1572 ic_data, entry, last);
1573 case MethodRecognizer::kByteArrayBaseSetUint8:
1574 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1575 kTypedDataUint8ArrayCid,
1576 ic_data, entry, last);
1577 case MethodRecognizer::kByteArrayBaseSetInt16:
1578 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1579 kTypedDataInt16ArrayCid,
1580 ic_data, entry, last);
1581 case MethodRecognizer::kByteArrayBaseSetUint16:
1582 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1583 kTypedDataUint16ArrayCid,
1584 ic_data, entry, last);
1585 case MethodRecognizer::kByteArrayBaseSetInt32:
1586 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1587 kTypedDataInt32ArrayCid,
1588 ic_data, entry, last);
1589 case MethodRecognizer::kByteArrayBaseSetUint32:
1590 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1591 kTypedDataUint32ArrayCid,
1592 ic_data, entry, last);
1593 case MethodRecognizer::kByteArrayBaseSetFloat32:
1594 if (!CanUnboxDouble()) {
1595 return false;
1596 }
1597 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1598 kTypedDataFloat32ArrayCid,
1599 ic_data, entry, last);
1600 case MethodRecognizer::kByteArrayBaseSetFloat64:
1601 if (!CanUnboxDouble()) {
1602 return false;
1603 }
1604 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1605 kTypedDataFloat64ArrayCid,
1606 ic_data, entry, last);
1607 case MethodRecognizer::kByteArrayBaseSetFloat32x4:
1608 if (!ShouldInlineSimd()) {
1609 return false;
1610 }
1611 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1612 kTypedDataFloat32x4ArrayCid,
1613 ic_data, entry, last);
1614 case MethodRecognizer::kByteArrayBaseSetInt32x4:
1615 if (!ShouldInlineSimd()) {
1616 return false;
1617 }
1618 return InlineByteArrayViewStore(target, call, receiver, receiver_cid,
1619 kTypedDataInt32x4ArrayCid,
1620 ic_data, entry, last);
1621 case MethodRecognizer::kStringBaseCodeUnitAt:
1622 return InlineStringCodeUnitAt(call, receiver_cid, entry, last);
1623 case MethodRecognizer::kStringBaseCharAt:
1624 return InlineStringBaseCharAt(call, receiver_cid, entry, last);
1625 case MethodRecognizer::kDoubleAdd:
1626 return InlineDoubleOp(Token::kADD, call, entry, last);
1627 case MethodRecognizer::kDoubleSub:
1628 return InlineDoubleOp(Token::kSUB, call, entry, last);
1629 case MethodRecognizer::kDoubleMul:
1630 return InlineDoubleOp(Token::kMUL, call, entry, last);
1631 case MethodRecognizer::kDoubleDiv:
1632 return InlineDoubleOp(Token::kDIV, call, entry, last);
1633 default:
1634 return false;
1635 }
1636 }
1637
1638
1639 intptr_t FlowGraphOptimizer::PrepareInlineIndexedOp(Instruction* call,
1640 intptr_t array_cid,
1641 Definition** array,
1642 Definition* index,
1643 Instruction** cursor) {
1644 // Insert index smi check.
1645 *cursor = flow_graph()->AppendTo(
1646 *cursor,
1647 new(I) CheckSmiInstr(new(I) Value(index),
1648 call->deopt_id(),
1649 call->token_pos()),
1650 call->env(),
1651 FlowGraph::kEffect);
1652
1653 // Insert array length load and bounds check.
1654 LoadFieldInstr* length =
1655 new(I) LoadFieldInstr(
1656 new(I) Value(*array),
1657 CheckArrayBoundInstr::LengthOffsetFor(array_cid),
1658 Type::ZoneHandle(I, Type::SmiType()),
1659 call->token_pos());
1660 length->set_is_immutable(
1661 CheckArrayBoundInstr::IsFixedLengthArrayType(array_cid));
1662 length->set_result_cid(kSmiCid);
1663 length->set_recognized_kind(
1664 LoadFieldInstr::RecognizedKindFromArrayCid(array_cid));
1665 *cursor = flow_graph()->AppendTo(*cursor,
1666 length,
1667 NULL,
1668 FlowGraph::kValue);
1669
1670 *cursor = flow_graph()->AppendTo(*cursor,
1671 new(I) CheckArrayBoundInstr(
1672 new(I) Value(length),
1673 new(I) Value(index),
1674 call->deopt_id()),
1675 call->env(),
1676 FlowGraph::kEffect);
1677
1678 if (array_cid == kGrowableObjectArrayCid) {
1679 // Insert data elements load.
1680 LoadFieldInstr* elements =
1681 new(I) LoadFieldInstr(
1682 new(I) Value(*array),
1683 GrowableObjectArray::data_offset(),
1684 Type::ZoneHandle(I, Type::DynamicType()),
1685 call->token_pos());
1686 elements->set_result_cid(kArrayCid);
1687 *cursor = flow_graph()->AppendTo(*cursor,
1688 elements,
1689 NULL,
1690 FlowGraph::kValue);
1691 // Load from the data from backing store which is a fixed-length array.
1692 *array = elements;
1693 array_cid = kArrayCid;
1694 } else if (RawObject::IsExternalTypedDataClassId(array_cid)) {
1695 LoadUntaggedInstr* elements =
1696 new(I) LoadUntaggedInstr(new(I) Value(*array),
1697 ExternalTypedData::data_offset());
1698 *cursor = flow_graph()->AppendTo(*cursor,
1699 elements,
1700 NULL,
1701 FlowGraph::kValue);
1702 *array = elements;
1703 }
1704 return array_cid;
1705 }
1706
1707
1708 bool FlowGraphOptimizer::InlineGetIndexed(MethodRecognizer::Kind kind,
1709 Instruction* call,
1710 Definition* receiver,
1711 const ICData& ic_data,
1712 TargetEntryInstr** entry,
1713 Definition** last) {
1714 intptr_t array_cid = MethodKindToCid(kind);
1715 ASSERT(array_cid != kIllegalCid);
1716
1717 Definition* array = receiver;
1718 Definition* index = call->ArgumentAt(1);
1719 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
1720 call->GetBlock()->try_index());
1721 (*entry)->InheritDeoptTarget(I, call);
1722 Instruction* cursor = *entry;
1723
1724 array_cid = PrepareInlineIndexedOp(call,
1725 array_cid,
1726 &array,
1727 index,
1728 &cursor);
1729
1730 intptr_t deopt_id = Isolate::kNoDeoptId;
1731 if ((array_cid == kTypedDataInt32ArrayCid) ||
1732 (array_cid == kTypedDataUint32ArrayCid)) {
1733 // Deoptimization may be needed if result does not always fit in a Smi.
1734 deopt_id = (kSmiBits >= 32) ? Isolate::kNoDeoptId : call->deopt_id();
1735 }
1736
1737 // Array load and return.
1738 intptr_t index_scale = Instance::ElementSizeFor(array_cid);
1739 *last = new(I) LoadIndexedInstr(new(I) Value(array),
1740 new(I) Value(index),
1741 index_scale,
1742 array_cid,
1743 deopt_id,
1744 call->token_pos());
1745 cursor = flow_graph()->AppendTo(
1746 cursor,
1747 *last,
1748 deopt_id != Isolate::kNoDeoptId ? call->env() : NULL,
1749 FlowGraph::kValue);
1750
1751 if (array_cid == kTypedDataFloat32ArrayCid) {
1752 *last = new(I) FloatToDoubleInstr(new(I) Value(*last), deopt_id);
1753 flow_graph()->AppendTo(cursor,
1754 *last,
1755 deopt_id != Isolate::kNoDeoptId ? call->env() : NULL,
1756 FlowGraph::kValue);
1757 }
1758 return true;
1759 }
1760
1761
1762 bool FlowGraphOptimizer::TryReplaceWithLoadIndexed(InstanceCallInstr* call) {
1763 // Check for monomorphic IC data.
1764 if (!call->HasICData()) return false;
1765 const ICData& ic_data =
1766 ICData::Handle(I, call->ic_data()->AsUnaryClassChecks());
1767 if (ic_data.NumberOfChecks() != 1) {
1768 return false;
1769 }
1770 ASSERT(ic_data.NumberOfUsedChecks() == 1);
1771 ASSERT(ic_data.HasOneTarget());
1772
1773 const Function& target = Function::Handle(I, ic_data.GetTargetAt(0));
1774 TargetEntryInstr* entry;
1775 Definition* last;
1776 if (!TryInlineRecognizedMethod(ic_data.GetReceiverClassIdAt(0),
1777 target,
1778 call,
1779 call->ArgumentAt(0),
1780 call->token_pos(),
1781 *call->ic_data(),
1782 &entry, &last)) {
1783 return false;
1784 }
1785
1786 // Insert receiver class check.
1787 AddReceiverCheck(call);
1788 // Remove the original push arguments.
1789 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
1790 PushArgumentInstr* push = call->PushArgumentAt(i);
1791 push->ReplaceUsesWith(push->value()->definition());
1792 push->RemoveFromGraph();
1793 }
1794 // Replace all uses of this definition with the result.
1795 call->ReplaceUsesWith(last);
1796 // Finally insert the sequence other definition in place of this one in the
1797 // graph.
1798 call->previous()->LinkTo(entry->next());
1799 entry->UnuseAllInputs(); // Entry block is not in the graph.
1800 last->LinkTo(call);
1801 // Remove through the iterator.
1802 ASSERT(current_iterator()->Current() == call);
1803 current_iterator()->RemoveCurrentFromGraph();
1804 call->set_previous(NULL);
1805 call->set_next(NULL);
1806 return true;
1807 }
1808
1809
1810 // Return true if d is a string of length one (a constant or result from
1811 // from string-from-char-code instruction.
1812 static bool IsLengthOneString(Definition* d) {
1813 if (d->IsConstant()) {
1814 const Object& obj = d->AsConstant()->value();
1815 if (obj.IsString()) {
1816 return String::Cast(obj).Length() == 1;
1817 } else {
1818 return false;
1819 }
1820 } else {
1821 return d->IsStringFromCharCode();
1822 }
1823 }
1824
1825
1826 // Returns true if the string comparison was converted into char-code
1827 // comparison. Conversion is only possible for strings of length one.
1828 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes.
1829 // TODO(srdjan): Expand for two-byte and external strings.
1830 bool FlowGraphOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call,
1831 Token::Kind op_kind) {
1832 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid));
1833 // Check that left and right are length one strings (either string constants
1834 // or results of string-from-char-code.
1835 Definition* left = call->ArgumentAt(0);
1836 Definition* right = call->ArgumentAt(1);
1837 Value* left_val = NULL;
1838 Definition* to_remove_left = NULL;
1839 if (IsLengthOneString(right)) {
1840 // Swap, since we know that both arguments are strings
1841 Definition* temp = left;
1842 left = right;
1843 right = temp;
1844 }
1845 if (IsLengthOneString(left)) {
1846 // Optimize if left is a string with length one (either constant or
1847 // result of string-from-char-code.
1848 if (left->IsConstant()) {
1849 ConstantInstr* left_const = left->AsConstant();
1850 const String& str = String::Cast(left_const->value());
1851 ASSERT(str.Length() == 1);
1852 ConstantInstr* char_code_left = flow_graph()->GetConstant(
1853 Smi::ZoneHandle(I, Smi::New(static_cast<intptr_t>(str.CharAt(0)))));
1854 left_val = new(I) Value(char_code_left);
1855 } else if (left->IsStringFromCharCode()) {
1856 // Use input of string-from-charcode as left value.
1857 StringFromCharCodeInstr* instr = left->AsStringFromCharCode();
1858 left_val = new(I) Value(instr->char_code()->definition());
1859 to_remove_left = instr;
1860 } else {
1861 // IsLengthOneString(left) should have been false.
1862 UNREACHABLE();
1863 }
1864
1865 Definition* to_remove_right = NULL;
1866 Value* right_val = NULL;
1867 if (right->IsStringFromCharCode()) {
1868 // Skip string-from-char-code, and use its input as right value.
1869 StringFromCharCodeInstr* right_instr = right->AsStringFromCharCode();
1870 right_val = new(I) Value(right_instr->char_code()->definition());
1871 to_remove_right = right_instr;
1872 } else {
1873 const ICData& unary_checks_1 =
1874 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecksForArgNr(1));
1875 AddCheckClass(right,
1876 unary_checks_1,
1877 call->deopt_id(),
1878 call->env(),
1879 call);
1880 // String-to-char-code instructions returns -1 (illegal charcode) if
1881 // string is not of length one.
1882 StringToCharCodeInstr* char_code_right =
1883 new(I) StringToCharCodeInstr(new(I) Value(right), kOneByteStringCid);
1884 InsertBefore(call, char_code_right, call->env(), FlowGraph::kValue);
1885 right_val = new(I) Value(char_code_right);
1886 }
1887
1888 // Comparing char-codes instead of strings.
1889 EqualityCompareInstr* comp =
1890 new(I) EqualityCompareInstr(call->token_pos(),
1891 op_kind,
1892 left_val,
1893 right_val,
1894 kSmiCid,
1895 call->deopt_id());
1896 ReplaceCall(call, comp);
1897
1898 // Remove dead instructions.
1899 if ((to_remove_left != NULL) &&
1900 (to_remove_left->input_use_list() == NULL)) {
1901 to_remove_left->ReplaceUsesWith(flow_graph()->constant_null());
1902 to_remove_left->RemoveFromGraph();
1903 }
1904 if ((to_remove_right != NULL) &&
1905 (to_remove_right->input_use_list() == NULL)) {
1906 to_remove_right->ReplaceUsesWith(flow_graph()->constant_null());
1907 to_remove_right->RemoveFromGraph();
1908 }
1909 return true;
1910 }
1911 return false;
1912 }
1913
1914
1915 static bool SmiFitsInDouble() { return kSmiBits < 53; }
1916
1917 bool FlowGraphOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call,
1918 Token::Kind op_kind) {
1919 const ICData& ic_data = *call->ic_data();
1920 ASSERT(ic_data.NumArgsTested() == 2);
1921
1922 ASSERT(call->ArgumentCount() == 2);
1923 Definition* left = call->ArgumentAt(0);
1924 Definition* right = call->ArgumentAt(1);
1925
1926 intptr_t cid = kIllegalCid;
1927 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) {
1928 if (TryStringLengthOneEquality(call, op_kind)) {
1929 return true;
1930 } else {
1931 return false;
1932 }
1933 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) {
1934 InsertBefore(call,
1935 new(I) CheckSmiInstr(new(I) Value(left),
1936 call->deopt_id(),
1937 call->token_pos()),
1938 call->env(),
1939 FlowGraph::kEffect);
1940 InsertBefore(call,
1941 new(I) CheckSmiInstr(new(I) Value(right),
1942 call->deopt_id(),
1943 call->token_pos()),
1944 call->env(),
1945 FlowGraph::kEffect);
1946 cid = kSmiCid;
1947 } else if (HasTwoMintOrSmi(ic_data) &&
1948 FlowGraphCompiler::SupportsUnboxedMints()) {
1949 cid = kMintCid;
1950 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) {
1951 // Use double comparison.
1952 if (SmiFitsInDouble()) {
1953 cid = kDoubleCid;
1954 } else {
1955 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
1956 // We cannot use double comparison on two smis. Need polymorphic
1957 // call.
1958 return false;
1959 } else {
1960 InsertBefore(call,
1961 new(I) CheckEitherNonSmiInstr(
1962 new(I) Value(left),
1963 new(I) Value(right),
1964 call->deopt_id()),
1965 call->env(),
1966 FlowGraph::kEffect);
1967 cid = kDoubleCid;
1968 }
1969 }
1970 } else {
1971 // Check if ICDData contains checks with Smi/Null combinations. In that case
1972 // we can still emit the optimized Smi equality operation but need to add
1973 // checks for null or Smi.
1974 GrowableArray<intptr_t> smi_or_null(2);
1975 smi_or_null.Add(kSmiCid);
1976 smi_or_null.Add(kNullCid);
1977 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data,
1978 smi_or_null,
1979 smi_or_null)) {
1980 const ICData& unary_checks_0 =
1981 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecks());
1982 AddCheckClass(left,
1983 unary_checks_0,
1984 call->deopt_id(),
1985 call->env(),
1986 call);
1987
1988 const ICData& unary_checks_1 =
1989 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecksForArgNr(1));
1990 AddCheckClass(right,
1991 unary_checks_1,
1992 call->deopt_id(),
1993 call->env(),
1994 call);
1995 cid = kSmiCid;
1996 } else {
1997 // Shortcut for equality with null.
1998 ConstantInstr* right_const = right->AsConstant();
1999 ConstantInstr* left_const = left->AsConstant();
2000 if ((right_const != NULL && right_const->value().IsNull()) ||
2001 (left_const != NULL && left_const->value().IsNull())) {
2002 StrictCompareInstr* comp =
2003 new(I) StrictCompareInstr(call->token_pos(),
2004 Token::kEQ_STRICT,
2005 new(I) Value(left),
2006 new(I) Value(right),
2007 false); // No number check.
2008 ReplaceCall(call, comp);
2009 return true;
2010 }
2011 return false;
2012 }
2013 }
2014 ASSERT(cid != kIllegalCid);
2015 EqualityCompareInstr* comp = new(I) EqualityCompareInstr(call->token_pos(),
2016 op_kind,
2017 new(I) Value(left),
2018 new(I) Value(right),
2019 cid,
2020 call->deopt_id());
2021 ReplaceCall(call, comp);
2022 return true;
2023 }
2024
2025
2026 bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call,
2027 Token::Kind op_kind) {
2028 const ICData& ic_data = *call->ic_data();
2029 ASSERT(ic_data.NumArgsTested() == 2);
2030
2031 ASSERT(call->ArgumentCount() == 2);
2032 Definition* left = call->ArgumentAt(0);
2033 Definition* right = call->ArgumentAt(1);
2034
2035 intptr_t cid = kIllegalCid;
2036 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
2037 InsertBefore(call,
2038 new(I) CheckSmiInstr(new(I) Value(left),
2039 call->deopt_id(),
2040 call->token_pos()),
2041 call->env(),
2042 FlowGraph::kEffect);
2043 InsertBefore(call,
2044 new(I) CheckSmiInstr(new(I) Value(right),
2045 call->deopt_id(),
2046 call->token_pos()),
2047 call->env(),
2048 FlowGraph::kEffect);
2049 cid = kSmiCid;
2050 } else if (HasTwoMintOrSmi(ic_data) &&
2051 FlowGraphCompiler::SupportsUnboxedMints()) {
2052 cid = kMintCid;
2053 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) {
2054 // Use double comparison.
2055 if (SmiFitsInDouble()) {
2056 cid = kDoubleCid;
2057 } else {
2058 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
2059 // We cannot use double comparison on two smis. Need polymorphic
2060 // call.
2061 return false;
2062 } else {
2063 InsertBefore(call,
2064 new(I) CheckEitherNonSmiInstr(
2065 new(I) Value(left),
2066 new(I) Value(right),
2067 call->deopt_id()),
2068 call->env(),
2069 FlowGraph::kEffect);
2070 cid = kDoubleCid;
2071 }
2072 }
2073 } else {
2074 return false;
2075 }
2076 ASSERT(cid != kIllegalCid);
2077 RelationalOpInstr* comp = new(I) RelationalOpInstr(call->token_pos(),
2078 op_kind,
2079 new(I) Value(left),
2080 new(I) Value(right),
2081 cid,
2082 call->deopt_id());
2083 ReplaceCall(call, comp);
2084 return true;
2085 }
2086
2087
2088 bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
2089 Token::Kind op_kind) {
2090 intptr_t operands_type = kIllegalCid;
2091 ASSERT(call->HasICData());
2092 const ICData& ic_data = *call->ic_data();
2093 switch (op_kind) {
2094 case Token::kADD:
2095 case Token::kSUB:
2096 case Token::kMUL:
2097 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
2098 // Don't generate smi code if the IC data is marked because
2099 // of an overflow.
2100 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)
2101 ? kMintCid
2102 : kSmiCid;
2103 } else if (HasTwoMintOrSmi(ic_data) &&
2104 FlowGraphCompiler::SupportsUnboxedMints()) {
2105 // Don't generate mint code if the IC data is marked because of an
2106 // overflow.
2107 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) return false;
2108 operands_type = kMintCid;
2109 } else if (ShouldSpecializeForDouble(ic_data)) {
2110 operands_type = kDoubleCid;
2111 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) {
2112 operands_type = kFloat32x4Cid;
2113 } else if (HasOnlyTwoOf(ic_data, kInt32x4Cid)) {
2114 ASSERT(op_kind != Token::kMUL); // Int32x4 doesn't have a multiply op.
2115 operands_type = kInt32x4Cid;
2116 } else if (HasOnlyTwoOf(ic_data, kFloat64x2Cid)) {
2117 operands_type = kFloat64x2Cid;
2118 } else {
2119 return false;
2120 }
2121 break;
2122 case Token::kDIV:
2123 if (ShouldSpecializeForDouble(ic_data) ||
2124 HasOnlyTwoOf(ic_data, kSmiCid)) {
2125 operands_type = kDoubleCid;
2126 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) {
2127 operands_type = kFloat32x4Cid;
2128 } else if (HasOnlyTwoOf(ic_data, kFloat64x2Cid)) {
2129 operands_type = kFloat64x2Cid;
2130 } else {
2131 return false;
2132 }
2133 break;
2134 case Token::kBIT_AND:
2135 case Token::kBIT_OR:
2136 case Token::kBIT_XOR:
2137 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
2138 operands_type = kSmiCid;
2139 } else if (HasTwoMintOrSmi(ic_data)) {
2140 operands_type = kMintCid;
2141 } else if (HasOnlyTwoOf(ic_data, kInt32x4Cid)) {
2142 operands_type = kInt32x4Cid;
2143 } else {
2144 return false;
2145 }
2146 break;
2147 case Token::kSHR:
2148 case Token::kSHL:
2149 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
2150 // Left shift may overflow from smi into mint or big ints.
2151 // Don't generate smi code if the IC data is marked because
2152 // of an overflow.
2153 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
2154 return false;
2155 }
2156 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)
2157 ? kMintCid
2158 : kSmiCid;
2159 } else if (HasTwoMintOrSmi(ic_data) &&
2160 HasOnlyOneSmi(ICData::Handle(I,
2161 ic_data.AsUnaryClassChecksForArgNr(1)))) {
2162 // Don't generate mint code if the IC data is marked because of an
2163 // overflow.
2164 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
2165 return false;
2166 }
2167 // Check for smi/mint << smi or smi/mint >> smi.
2168 operands_type = kMintCid;
2169 } else {
2170 return false;
2171 }
2172 break;
2173 case Token::kMOD:
2174 case Token::kTRUNCDIV:
2175 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
2176 if (ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp)) {
2177 return false;
2178 }
2179 operands_type = kSmiCid;
2180 } else {
2181 return false;
2182 }
2183 break;
2184 default:
2185 UNREACHABLE();
2186 }
2187
2188 ASSERT(call->ArgumentCount() == 2);
2189 Definition* left = call->ArgumentAt(0);
2190 Definition* right = call->ArgumentAt(1);
2191 if (operands_type == kDoubleCid) {
2192 if (!CanUnboxDouble()) {
2193 return false;
2194 }
2195 // Check that either left or right are not a smi. Result of a
2196 // binary operation with two smis is a smi not a double, except '/' which
2197 // returns a double for two smis.
2198 if (op_kind != Token::kDIV) {
2199 InsertBefore(call,
2200 new(I) CheckEitherNonSmiInstr(
2201 new(I) Value(left),
2202 new(I) Value(right),
2203 call->deopt_id()),
2204 call->env(),
2205 FlowGraph::kEffect);
2206 }
2207
2208 BinaryDoubleOpInstr* double_bin_op =
2209 new(I) BinaryDoubleOpInstr(op_kind,
2210 new(I) Value(left),
2211 new(I) Value(right),
2212 call->deopt_id(), call->token_pos());
2213 ReplaceCall(call, double_bin_op);
2214 } else if (operands_type == kMintCid) {
2215 if (!FlowGraphCompiler::SupportsUnboxedMints()) return false;
2216 if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
2217 ShiftMintOpInstr* shift_op =
2218 new(I) ShiftMintOpInstr(
2219 op_kind, new(I) Value(left), new(I) Value(right),
2220 call->deopt_id());
2221 ReplaceCall(call, shift_op);
2222 } else {
2223 BinaryMintOpInstr* bin_op =
2224 new(I) BinaryMintOpInstr(
2225 op_kind, new(I) Value(left), new(I) Value(right),
2226 call->deopt_id());
2227 ReplaceCall(call, bin_op);
2228 }
2229 } else if (operands_type == kFloat32x4Cid) {
2230 return InlineFloat32x4BinaryOp(call, op_kind);
2231 } else if (operands_type == kInt32x4Cid) {
2232 return InlineInt32x4BinaryOp(call, op_kind);
2233 } else if (operands_type == kFloat64x2Cid) {
2234 return InlineFloat64x2BinaryOp(call, op_kind);
2235 } else if (op_kind == Token::kMOD) {
2236 ASSERT(operands_type == kSmiCid);
2237 if (right->IsConstant()) {
2238 const Object& obj = right->AsConstant()->value();
2239 if (obj.IsSmi() && Utils::IsPowerOfTwo(Smi::Cast(obj).Value())) {
2240 // Insert smi check and attach a copy of the original environment
2241 // because the smi operation can still deoptimize.
2242 InsertBefore(call,
2243 new(I) CheckSmiInstr(new(I) Value(left),
2244 call->deopt_id(),
2245 call->token_pos()),
2246 call->env(),
2247 FlowGraph::kEffect);
2248 ConstantInstr* constant =
2249 flow_graph()->GetConstant(Smi::Handle(I,
2250 Smi::New(Smi::Cast(obj).Value() - 1)));
2251 BinarySmiOpInstr* bin_op =
2252 new(I) BinarySmiOpInstr(Token::kBIT_AND,
2253 new(I) Value(left),
2254 new(I) Value(constant),
2255 call->deopt_id());
2256 ReplaceCall(call, bin_op);
2257 return true;
2258 }
2259 }
2260 // Insert two smi checks and attach a copy of the original
2261 // environment because the smi operation can still deoptimize.
2262 AddCheckSmi(left, call->deopt_id(), call->env(), call);
2263 AddCheckSmi(right, call->deopt_id(), call->env(), call);
2264 BinarySmiOpInstr* bin_op =
2265 new(I) BinarySmiOpInstr(op_kind,
2266 new(I) Value(left),
2267 new(I) Value(right),
2268 call->deopt_id());
2269 ReplaceCall(call, bin_op);
2270 } else {
2271 ASSERT(operands_type == kSmiCid);
2272 // Insert two smi checks and attach a copy of the original
2273 // environment because the smi operation can still deoptimize.
2274 AddCheckSmi(left, call->deopt_id(), call->env(), call);
2275 AddCheckSmi(right, call->deopt_id(), call->env(), call);
2276 if (left->IsConstant() &&
2277 ((op_kind == Token::kADD) || (op_kind == Token::kMUL))) {
2278 // Constant should be on the right side.
2279 Definition* temp = left;
2280 left = right;
2281 right = temp;
2282 }
2283 BinarySmiOpInstr* bin_op =
2284 new(I) BinarySmiOpInstr(
2285 op_kind,
2286 new(I) Value(left),
2287 new(I) Value(right),
2288 call->deopt_id());
2289 ReplaceCall(call, bin_op);
2290 }
2291 return true;
2292 }
2293
2294
2295 bool FlowGraphOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call,
2296 Token::Kind op_kind) {
2297 ASSERT(call->ArgumentCount() == 1);
2298 Definition* input = call->ArgumentAt(0);
2299 Definition* unary_op = NULL;
2300 if (HasOnlyOneSmi(*call->ic_data())) {
2301 InsertBefore(call,
2302 new(I) CheckSmiInstr(new(I) Value(input),
2303 call->deopt_id(),
2304 call->token_pos()),
2305 call->env(),
2306 FlowGraph::kEffect);
2307 unary_op = new(I) UnarySmiOpInstr(
2308 op_kind, new(I) Value(input), call->deopt_id());
2309 } else if ((op_kind == Token::kBIT_NOT) &&
2310 HasOnlySmiOrMint(*call->ic_data()) &&
2311 FlowGraphCompiler::SupportsUnboxedMints()) {
2312 unary_op = new(I) UnaryMintOpInstr(
2313 op_kind, new(I) Value(input), call->deopt_id());
2314 } else if (HasOnlyOneDouble(*call->ic_data()) &&
2315 (op_kind == Token::kNEGATE) &&
2316 CanUnboxDouble()) {
2317 AddReceiverCheck(call);
2318 unary_op = new(I) UnaryDoubleOpInstr(
2319 Token::kNEGATE, new(I) Value(input), call->deopt_id());
2320 } else {
2321 return false;
2322 }
2323 ASSERT(unary_op != NULL);
2324 ReplaceCall(call, unary_op);
2325 return true;
2326 }
2327
2328
2329 // Using field class
2330 static RawField* GetField(intptr_t class_id, const String& field_name) {
2331 Isolate* isolate = Isolate::Current();
2332 Class& cls = Class::Handle(isolate, isolate->class_table()->At(class_id));
2333 Field& field = Field::Handle(isolate);
2334 while (!cls.IsNull()) {
2335 field = cls.LookupInstanceField(field_name);
2336 if (!field.IsNull()) {
2337 return field.raw();
2338 }
2339 cls = cls.SuperClass();
2340 }
2341 return Field::null();
2342 }
2343
2344
2345 // Use CHA to determine if the call needs a class check: if the callee's
2346 // receiver is the same as the caller's receiver and there are no overriden
2347 // callee functions, then no class check is needed.
2348 bool FlowGraphOptimizer::InstanceCallNeedsClassCheck(
2349 InstanceCallInstr* call, RawFunction::Kind kind) const {
2350 if (!FLAG_use_cha) return true;
2351 Definition* callee_receiver = call->ArgumentAt(0);
2352 ASSERT(callee_receiver != NULL);
2353 const Function& function = flow_graph_->parsed_function()->function();
2354 if (function.IsDynamicFunction() &&
2355 callee_receiver->IsParameter() &&
2356 (callee_receiver->AsParameter()->index() == 0)) {
2357 const String& name = (kind == RawFunction::kMethodExtractor)
2358 ? String::Handle(I, Field::NameFromGetter(call->function_name()))
2359 : call->function_name();
2360 return isolate()->cha()->HasOverride(Class::Handle(I, function.Owner()),
2361 name);
2362 }
2363 return true;
2364 }
2365
2366
2367 void FlowGraphOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call) {
2368 ASSERT(call->HasICData());
2369 const ICData& ic_data = *call->ic_data();
2370 ASSERT(ic_data.HasOneTarget());
2371 Function& target = Function::Handle(I);
2372 GrowableArray<intptr_t> class_ids;
2373 ic_data.GetCheckAt(0, &class_ids, &target);
2374 ASSERT(class_ids.length() == 1);
2375 // Inline implicit instance getter.
2376 const String& field_name =
2377 String::Handle(I, Field::NameFromGetter(call->function_name()));
2378 const Field& field =
2379 Field::ZoneHandle(I, GetField(class_ids[0], field_name));
2380 ASSERT(!field.IsNull());
2381
2382 if (InstanceCallNeedsClassCheck(call, RawFunction::kImplicitGetter)) {
2383 AddReceiverCheck(call);
2384 }
2385 LoadFieldInstr* load = new(I) LoadFieldInstr(
2386 new(I) Value(call->ArgumentAt(0)),
2387 &field,
2388 AbstractType::ZoneHandle(I, field.type()),
2389 call->token_pos());
2390 load->set_is_immutable(field.is_final());
2391 if (field.guarded_cid() != kIllegalCid) {
2392 if (!field.is_nullable() || (field.guarded_cid() == kNullCid)) {
2393 load->set_result_cid(field.guarded_cid());
2394 }
2395 FlowGraph::AddToGuardedFields(flow_graph_->guarded_fields(), &field);
2396 }
2397
2398 // Discard the environment from the original instruction because the load
2399 // can't deoptimize.
2400 call->RemoveEnvironment();
2401 ReplaceCall(call, load);
2402
2403 if (load->result_cid() != kDynamicCid) {
2404 // Reset value types if guarded_cid was used.
2405 for (Value::Iterator it(load->input_use_list());
2406 !it.Done();
2407 it.Advance()) {
2408 it.Current()->SetReachingType(NULL);
2409 }
2410 }
2411 }
2412
2413
2414 bool FlowGraphOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call,
2415 MethodRecognizer::Kind getter) {
2416 if (!ShouldInlineSimd()) {
2417 return false;
2418 }
2419 AddCheckClass(call->ArgumentAt(0),
2420 ICData::ZoneHandle(
2421 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2422 call->deopt_id(),
2423 call->env(),
2424 call);
2425 intptr_t mask = 0;
2426 if ((getter == MethodRecognizer::kFloat32x4Shuffle) ||
2427 (getter == MethodRecognizer::kFloat32x4ShuffleMix)) {
2428 // Extract shuffle mask.
2429 Definition* mask_definition = NULL;
2430 if (getter == MethodRecognizer::kFloat32x4Shuffle) {
2431 ASSERT(call->ArgumentCount() == 2);
2432 mask_definition = call->ArgumentAt(1);
2433 } else {
2434 ASSERT(getter == MethodRecognizer::kFloat32x4ShuffleMix);
2435 ASSERT(call->ArgumentCount() == 3);
2436 mask_definition = call->ArgumentAt(2);
2437 }
2438 if (!mask_definition->IsConstant()) {
2439 return false;
2440 }
2441 ASSERT(mask_definition->IsConstant());
2442 ConstantInstr* constant_instruction = mask_definition->AsConstant();
2443 const Object& constant_mask = constant_instruction->value();
2444 if (!constant_mask.IsSmi()) {
2445 return false;
2446 }
2447 ASSERT(constant_mask.IsSmi());
2448 mask = Smi::Cast(constant_mask).Value();
2449 if ((mask < 0) || (mask > 255)) {
2450 // Not a valid mask.
2451 return false;
2452 }
2453 }
2454 if (getter == MethodRecognizer::kFloat32x4GetSignMask) {
2455 Simd32x4GetSignMaskInstr* instr = new(I) Simd32x4GetSignMaskInstr(
2456 getter,
2457 new(I) Value(call->ArgumentAt(0)),
2458 call->deopt_id());
2459 ReplaceCall(call, instr);
2460 return true;
2461 } else if (getter == MethodRecognizer::kFloat32x4ShuffleMix) {
2462 Simd32x4ShuffleMixInstr* instr = new(I) Simd32x4ShuffleMixInstr(
2463 getter,
2464 new(I) Value(call->ArgumentAt(0)),
2465 new(I) Value(call->ArgumentAt(1)),
2466 mask,
2467 call->deopt_id());
2468 ReplaceCall(call, instr);
2469 return true;
2470 } else {
2471 ASSERT((getter == MethodRecognizer::kFloat32x4Shuffle) ||
2472 (getter == MethodRecognizer::kFloat32x4ShuffleX) ||
2473 (getter == MethodRecognizer::kFloat32x4ShuffleY) ||
2474 (getter == MethodRecognizer::kFloat32x4ShuffleZ) ||
2475 (getter == MethodRecognizer::kFloat32x4ShuffleW));
2476 Simd32x4ShuffleInstr* instr = new(I) Simd32x4ShuffleInstr(
2477 getter,
2478 new(I) Value(call->ArgumentAt(0)),
2479 mask,
2480 call->deopt_id());
2481 ReplaceCall(call, instr);
2482 return true;
2483 }
2484 UNREACHABLE();
2485 return false;
2486 }
2487
2488
2489 bool FlowGraphOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call,
2490 MethodRecognizer::Kind getter) {
2491 if (!ShouldInlineSimd()) {
2492 return false;
2493 }
2494 AddCheckClass(call->ArgumentAt(0),
2495 ICData::ZoneHandle(
2496 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2497 call->deopt_id(),
2498 call->env(),
2499 call);
2500 if ((getter == MethodRecognizer::kFloat64x2GetX) ||
2501 (getter == MethodRecognizer::kFloat64x2GetY)) {
2502 Simd64x2ShuffleInstr* instr = new(I) Simd64x2ShuffleInstr(
2503 getter,
2504 new(I) Value(call->ArgumentAt(0)),
2505 0,
2506 call->deopt_id());
2507 ReplaceCall(call, instr);
2508 return true;
2509 }
2510 UNREACHABLE();
2511 return false;
2512 }
2513
2514
2515 bool FlowGraphOptimizer::InlineInt32x4Getter(InstanceCallInstr* call,
2516 MethodRecognizer::Kind getter) {
2517 if (!ShouldInlineSimd()) {
2518 return false;
2519 }
2520 AddCheckClass(call->ArgumentAt(0),
2521 ICData::ZoneHandle(
2522 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2523 call->deopt_id(),
2524 call->env(),
2525 call);
2526 intptr_t mask = 0;
2527 if ((getter == MethodRecognizer::kInt32x4Shuffle) ||
2528 (getter == MethodRecognizer::kInt32x4ShuffleMix)) {
2529 // Extract shuffle mask.
2530 Definition* mask_definition = NULL;
2531 if (getter == MethodRecognizer::kInt32x4Shuffle) {
2532 ASSERT(call->ArgumentCount() == 2);
2533 mask_definition = call->ArgumentAt(1);
2534 } else {
2535 ASSERT(getter == MethodRecognizer::kInt32x4ShuffleMix);
2536 ASSERT(call->ArgumentCount() == 3);
2537 mask_definition = call->ArgumentAt(2);
2538 }
2539 if (!mask_definition->IsConstant()) {
2540 return false;
2541 }
2542 ASSERT(mask_definition->IsConstant());
2543 ConstantInstr* constant_instruction = mask_definition->AsConstant();
2544 const Object& constant_mask = constant_instruction->value();
2545 if (!constant_mask.IsSmi()) {
2546 return false;
2547 }
2548 ASSERT(constant_mask.IsSmi());
2549 mask = Smi::Cast(constant_mask).Value();
2550 if ((mask < 0) || (mask > 255)) {
2551 // Not a valid mask.
2552 return false;
2553 }
2554 }
2555 if (getter == MethodRecognizer::kInt32x4GetSignMask) {
2556 Simd32x4GetSignMaskInstr* instr = new(I) Simd32x4GetSignMaskInstr(
2557 getter,
2558 new(I) Value(call->ArgumentAt(0)),
2559 call->deopt_id());
2560 ReplaceCall(call, instr);
2561 return true;
2562 } else if (getter == MethodRecognizer::kInt32x4ShuffleMix) {
2563 Simd32x4ShuffleMixInstr* instr = new(I) Simd32x4ShuffleMixInstr(
2564 getter,
2565 new(I) Value(call->ArgumentAt(0)),
2566 new(I) Value(call->ArgumentAt(1)),
2567 mask,
2568 call->deopt_id());
2569 ReplaceCall(call, instr);
2570 return true;
2571 } else if (getter == MethodRecognizer::kInt32x4Shuffle) {
2572 Simd32x4ShuffleInstr* instr = new(I) Simd32x4ShuffleInstr(
2573 getter,
2574 new(I) Value(call->ArgumentAt(0)),
2575 mask,
2576 call->deopt_id());
2577 ReplaceCall(call, instr);
2578 return true;
2579 } else {
2580 Int32x4GetFlagInstr* instr = new(I) Int32x4GetFlagInstr(
2581 getter,
2582 new(I) Value(call->ArgumentAt(0)),
2583 call->deopt_id());
2584 ReplaceCall(call, instr);
2585 return true;
2586 }
2587 }
2588
2589
2590 bool FlowGraphOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call,
2591 Token::Kind op_kind) {
2592 if (!ShouldInlineSimd()) {
2593 return false;
2594 }
2595 ASSERT(call->ArgumentCount() == 2);
2596 Definition* left = call->ArgumentAt(0);
2597 Definition* right = call->ArgumentAt(1);
2598 // Type check left.
2599 AddCheckClass(left,
2600 ICData::ZoneHandle(
2601 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2602 call->deopt_id(),
2603 call->env(),
2604 call);
2605 // Type check right.
2606 AddCheckClass(right,
2607 ICData::ZoneHandle(
2608 I, call->ic_data()->AsUnaryClassChecksForArgNr(1)),
2609 call->deopt_id(),
2610 call->env(),
2611 call);
2612 // Replace call.
2613 BinaryFloat32x4OpInstr* float32x4_bin_op =
2614 new(I) BinaryFloat32x4OpInstr(
2615 op_kind, new(I) Value(left), new(I) Value(right),
2616 call->deopt_id());
2617 ReplaceCall(call, float32x4_bin_op);
2618
2619 return true;
2620 }
2621
2622
2623 bool FlowGraphOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call,
2624 Token::Kind op_kind) {
2625 if (!ShouldInlineSimd()) {
2626 return false;
2627 }
2628 ASSERT(call->ArgumentCount() == 2);
2629 Definition* left = call->ArgumentAt(0);
2630 Definition* right = call->ArgumentAt(1);
2631 // Type check left.
2632 AddCheckClass(left,
2633 ICData::ZoneHandle(
2634 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2635 call->deopt_id(),
2636 call->env(),
2637 call);
2638 // Type check right.
2639 AddCheckClass(right,
2640 ICData::ZoneHandle(I,
2641 call->ic_data()->AsUnaryClassChecksForArgNr(1)),
2642 call->deopt_id(),
2643 call->env(),
2644 call);
2645 // Replace call.
2646 BinaryInt32x4OpInstr* int32x4_bin_op =
2647 new(I) BinaryInt32x4OpInstr(
2648 op_kind, new(I) Value(left), new(I) Value(right),
2649 call->deopt_id());
2650 ReplaceCall(call, int32x4_bin_op);
2651 return true;
2652 }
2653
2654
2655 bool FlowGraphOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call,
2656 Token::Kind op_kind) {
2657 if (!ShouldInlineSimd()) {
2658 return false;
2659 }
2660 ASSERT(call->ArgumentCount() == 2);
2661 Definition* left = call->ArgumentAt(0);
2662 Definition* right = call->ArgumentAt(1);
2663 // Type check left.
2664 AddCheckClass(left,
2665 ICData::ZoneHandle(
2666 call->ic_data()->AsUnaryClassChecksForArgNr(0)),
2667 call->deopt_id(),
2668 call->env(),
2669 call);
2670 // Type check right.
2671 AddCheckClass(right,
2672 ICData::ZoneHandle(
2673 call->ic_data()->AsUnaryClassChecksForArgNr(1)),
2674 call->deopt_id(),
2675 call->env(),
2676 call);
2677 // Replace call.
2678 BinaryFloat64x2OpInstr* float64x2_bin_op =
2679 new(I) BinaryFloat64x2OpInstr(
2680 op_kind, new(I) Value(left), new(I) Value(right),
2681 call->deopt_id());
2682 ReplaceCall(call, float64x2_bin_op);
2683 return true;
2684 }
2685
2686
2687 // Only unique implicit instance getters can be currently handled.
2688 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) {
2689 ASSERT(call->HasICData());
2690 const ICData& ic_data = *call->ic_data();
2691 if (ic_data.NumberOfUsedChecks() == 0) {
2692 // No type feedback collected.
2693 return false;
2694 }
2695
2696 if (!ic_data.HasOneTarget()) {
2697 // Polymorphic sites are inlined like normal methods by conventional
2698 // inlining in FlowGraphInliner.
2699 return false;
2700 }
2701
2702 const Function& target = Function::Handle(I, ic_data.GetTargetAt(0));
2703 if (target.kind() != RawFunction::kImplicitGetter) {
2704 // Non-implicit getters are inlined like normal methods by conventional
2705 // inlining in FlowGraphInliner.
2706 return false;
2707 }
2708 InlineImplicitInstanceGetter(call);
2709 return true;
2710 }
2711
2712
2713 bool FlowGraphOptimizer::TryReplaceInstanceCallWithInline(
2714 InstanceCallInstr* call) {
2715 ASSERT(call->HasICData());
2716 Function& target = Function::Handle(I);
2717 GrowableArray<intptr_t> class_ids;
2718 call->ic_data()->GetCheckAt(0, &class_ids, &target);
2719 const intptr_t receiver_cid = class_ids[0];
2720
2721 TargetEntryInstr* entry;
2722 Definition* last;
2723 if (!TryInlineRecognizedMethod(receiver_cid,
2724 target,
2725 call,
2726 call->ArgumentAt(0),
2727 call->token_pos(),
2728 *call->ic_data(),
2729 &entry, &last)) {
2730 return false;
2731 }
2732
2733 // Insert receiver class check.
2734 AddReceiverCheck(call);
2735 // Remove the original push arguments.
2736 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
2737 PushArgumentInstr* push = call->PushArgumentAt(i);
2738 push->ReplaceUsesWith(push->value()->definition());
2739 push->RemoveFromGraph();
2740 }
2741 // Replace all uses of this definition with the result.
2742 call->ReplaceUsesWith(last);
2743 // Finally insert the sequence other definition in place of this one in the
2744 // graph.
2745 call->previous()->LinkTo(entry->next());
2746 entry->UnuseAllInputs(); // Entry block is not in the graph.
2747 last->LinkTo(call);
2748 // Remove through the iterator.
2749 ASSERT(current_iterator()->Current() == call);
2750 current_iterator()->RemoveCurrentFromGraph();
2751 call->set_previous(NULL);
2752 call->set_next(NULL);
2753 return true;
2754 }
2755
2756
2757 // Returns the LoadIndexedInstr.
2758 Definition* FlowGraphOptimizer::PrepareInlineStringIndexOp(
2759 Instruction* call,
2760 intptr_t cid,
2761 Definition* str,
2762 Definition* index,
2763 Instruction* cursor) {
2764
2765 cursor = flow_graph()->AppendTo(cursor,
2766 new(I) CheckSmiInstr(
2767 new(I) Value(index),
2768 call->deopt_id(),
2769 call->token_pos()),
2770 call->env(),
2771 FlowGraph::kEffect);
2772
2773 // Load the length of the string.
2774 // Treat length loads as mutable (i.e. affected by side effects) to avoid
2775 // hoisting them since we can't hoist the preceding class-check. This
2776 // is because of externalization of strings that affects their class-id.
2777 LoadFieldInstr* length = new(I) LoadFieldInstr(
2778 new(I) Value(str),
2779 String::length_offset(),
2780 Type::ZoneHandle(I, Type::SmiType()),
2781 str->token_pos());
2782 length->set_result_cid(kSmiCid);
2783 length->set_recognized_kind(MethodRecognizer::kStringBaseLength);
2784
2785 cursor = flow_graph()->AppendTo(cursor, length, NULL, FlowGraph::kValue);
2786 // Bounds check.
2787 cursor = flow_graph()->AppendTo(cursor,
2788 new(I) CheckArrayBoundInstr(
2789 new(I) Value(length),
2790 new(I) Value(index),
2791 call->deopt_id()),
2792 call->env(),
2793 FlowGraph::kEffect);
2794
2795 LoadIndexedInstr* load_indexed = new(I) LoadIndexedInstr(
2796 new(I) Value(str),
2797 new(I) Value(index),
2798 Instance::ElementSizeFor(cid),
2799 cid,
2800 Isolate::kNoDeoptId,
2801 call->token_pos());
2802
2803 cursor = flow_graph()->AppendTo(cursor,
2804 load_indexed,
2805 NULL,
2806 FlowGraph::kValue);
2807 ASSERT(cursor == load_indexed);
2808 return load_indexed;
2809 }
2810
2811
2812 bool FlowGraphOptimizer::InlineStringCodeUnitAt(
2813 Instruction* call,
2814 intptr_t cid,
2815 TargetEntryInstr** entry,
2816 Definition** last) {
2817 // TODO(johnmccutchan): Handle external strings in PrepareInlineStringIndexOp.
2818 if (RawObject::IsExternalStringClassId(cid)) {
2819 return false;
2820 }
2821
2822 Definition* str = call->ArgumentAt(0);
2823 Definition* index = call->ArgumentAt(1);
2824
2825 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
2826 call->GetBlock()->try_index());
2827 (*entry)->InheritDeoptTarget(I, call);
2828
2829 *last = PrepareInlineStringIndexOp(call, cid, str, index, *entry);
2830
2831 return true;
2832 }
2833
2834
2835 bool FlowGraphOptimizer::InlineStringBaseCharAt(
2836 Instruction* call,
2837 intptr_t cid,
2838 TargetEntryInstr** entry,
2839 Definition** last) {
2840 // TODO(johnmccutchan): Handle external strings in PrepareInlineStringIndexOp.
2841 if (RawObject::IsExternalStringClassId(cid) || cid != kOneByteStringCid) {
2842 return false;
2843 }
2844 Definition* str = call->ArgumentAt(0);
2845 Definition* index = call->ArgumentAt(1);
2846
2847 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
2848 call->GetBlock()->try_index());
2849 (*entry)->InheritDeoptTarget(I, call);
2850
2851 *last = PrepareInlineStringIndexOp(call, cid, str, index, *entry);
2852
2853 StringFromCharCodeInstr* char_at = new(I) StringFromCharCodeInstr(
2854 new(I) Value(*last), cid);
2855
2856 flow_graph()->AppendTo(*last, char_at, NULL, FlowGraph::kValue);
2857 *last = char_at;
2858
2859 return true;
2860 }
2861
2862
2863 bool FlowGraphOptimizer::InlineDoubleOp(
2864 Token::Kind op_kind,
2865 Instruction* call,
2866 TargetEntryInstr** entry,
2867 Definition** last) {
2868 Definition* left = call->ArgumentAt(0);
2869 Definition* right = call->ArgumentAt(1);
2870
2871 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
2872 call->GetBlock()->try_index());
2873 (*entry)->InheritDeoptTarget(I, call);
2874 // Arguments are checked. No need for class check.
2875 BinaryDoubleOpInstr* double_bin_op =
2876 new(I) BinaryDoubleOpInstr(op_kind,
2877 new(I) Value(left),
2878 new(I) Value(right),
2879 call->deopt_id(), call->token_pos());
2880 flow_graph()->AppendTo(*entry, double_bin_op, call->env(), FlowGraph::kValue);
2881 *last = double_bin_op;
2882
2883 return true;
2884 }
2885
2886
2887 void FlowGraphOptimizer::ReplaceWithMathCFunction(
2888 InstanceCallInstr* call,
2889 MethodRecognizer::Kind recognized_kind) {
2890 AddReceiverCheck(call);
2891 ZoneGrowableArray<Value*>* args =
2892 new(I) ZoneGrowableArray<Value*>(call->ArgumentCount());
2893 for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
2894 args->Add(new(I) Value(call->ArgumentAt(i)));
2895 }
2896 InvokeMathCFunctionInstr* invoke =
2897 new(I) InvokeMathCFunctionInstr(args,
2898 call->deopt_id(),
2899 recognized_kind,
2900 call->token_pos());
2901 ReplaceCall(call, invoke);
2902 }
2903
2904
2905 static bool IsSupportedByteArrayViewCid(intptr_t cid) {
2906 switch (cid) {
2907 case kTypedDataInt8ArrayCid:
2908 case kTypedDataUint8ArrayCid:
2909 case kExternalTypedDataUint8ArrayCid:
2910 case kTypedDataUint8ClampedArrayCid:
2911 case kExternalTypedDataUint8ClampedArrayCid:
2912 case kTypedDataInt16ArrayCid:
2913 case kTypedDataUint16ArrayCid:
2914 case kTypedDataInt32ArrayCid:
2915 case kTypedDataUint32ArrayCid:
2916 case kTypedDataFloat32ArrayCid:
2917 case kTypedDataFloat64ArrayCid:
2918 case kTypedDataFloat32x4ArrayCid:
2919 case kTypedDataInt32x4ArrayCid:
2920 return true;
2921 default:
2922 return false;
2923 }
2924 }
2925
2926
2927 // Inline only simple, frequently called core library methods.
2928 bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) {
2929 ASSERT(call->HasICData());
2930 const ICData& ic_data = *call->ic_data();
2931 if ((ic_data.NumberOfUsedChecks() == 0) || !ic_data.HasOneTarget()) {
2932 // No type feedback collected or multiple targets found.
2933 return false;
2934 }
2935
2936 Function& target = Function::Handle(I);
2937 GrowableArray<intptr_t> class_ids;
2938 ic_data.GetCheckAt(0, &class_ids, &target);
2939 MethodRecognizer::Kind recognized_kind =
2940 MethodRecognizer::RecognizeKind(target);
2941
2942 if ((recognized_kind == MethodRecognizer::kGrowableArraySetData) &&
2943 (ic_data.NumberOfChecks() == 1) &&
2944 (class_ids[0] == kGrowableObjectArrayCid)) {
2945 // This is an internal method, no need to check argument types.
2946 Definition* array = call->ArgumentAt(0);
2947 Definition* value = call->ArgumentAt(1);
2948 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
2949 GrowableObjectArray::data_offset(),
2950 new(I) Value(array),
2951 new(I) Value(value),
2952 kEmitStoreBarrier,
2953 call->token_pos());
2954 ReplaceCall(call, store);
2955 return true;
2956 }
2957
2958 if ((recognized_kind == MethodRecognizer::kGrowableArraySetLength) &&
2959 (ic_data.NumberOfChecks() == 1) &&
2960 (class_ids[0] == kGrowableObjectArrayCid)) {
2961 // This is an internal method, no need to check argument types nor
2962 // range.
2963 Definition* array = call->ArgumentAt(0);
2964 Definition* value = call->ArgumentAt(1);
2965 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
2966 GrowableObjectArray::length_offset(),
2967 new(I) Value(array),
2968 new(I) Value(value),
2969 kNoStoreBarrier,
2970 call->token_pos());
2971 ReplaceCall(call, store);
2972 return true;
2973 }
2974
2975 if (((recognized_kind == MethodRecognizer::kStringBaseCodeUnitAt) ||
2976 (recognized_kind == MethodRecognizer::kStringBaseCharAt)) &&
2977 (ic_data.NumberOfChecks() == 1) &&
2978 ((class_ids[0] == kOneByteStringCid) ||
2979 (class_ids[0] == kTwoByteStringCid))) {
2980 return TryReplaceInstanceCallWithInline(call);
2981 }
2982
2983 if ((class_ids[0] == kOneByteStringCid) && (ic_data.NumberOfChecks() == 1)) {
2984 if (recognized_kind == MethodRecognizer::kOneByteStringSetAt) {
2985 // This is an internal method, no need to check argument types nor
2986 // range.
2987 Definition* str = call->ArgumentAt(0);
2988 Definition* index = call->ArgumentAt(1);
2989 Definition* value = call->ArgumentAt(2);
2990 StoreIndexedInstr* store_op = new(I) StoreIndexedInstr(
2991 new(I) Value(str),
2992 new(I) Value(index),
2993 new(I) Value(value),
2994 kNoStoreBarrier,
2995 1, // Index scale
2996 kOneByteStringCid,
2997 call->deopt_id(),
2998 call->token_pos());
2999 ReplaceCall(call, store_op);
3000 return true;
3001 }
3002 return false;
3003 }
3004
3005 if (CanUnboxDouble() &&
3006 (recognized_kind == MethodRecognizer::kIntegerToDouble) &&
3007 (ic_data.NumberOfChecks() == 1)) {
3008 if (class_ids[0] == kSmiCid) {
3009 AddReceiverCheck(call);
3010 ReplaceCall(call,
3011 new(I) SmiToDoubleInstr(
3012 new(I) Value(call->ArgumentAt(0)),
3013 call->token_pos()));
3014 return true;
3015 } else if ((class_ids[0] == kMintCid) && CanConvertUnboxedMintToDouble()) {
3016 AddReceiverCheck(call);
3017 ReplaceCall(call,
3018 new(I) MintToDoubleInstr(new(I) Value(call->ArgumentAt(0)),
3019 call->deopt_id()));
3020 return true;
3021 }
3022 }
3023
3024 if (class_ids[0] == kDoubleCid) {
3025 if (!CanUnboxDouble()) {
3026 return false;
3027 }
3028 switch (recognized_kind) {
3029 case MethodRecognizer::kDoubleToInteger: {
3030 AddReceiverCheck(call);
3031 ASSERT(call->HasICData());
3032 const ICData& ic_data = *call->ic_data();
3033 Definition* input = call->ArgumentAt(0);
3034 Definition* d2i_instr = NULL;
3035 if (ic_data.HasDeoptReason(ICData::kDeoptDoubleToSmi)) {
3036 // Do not repeatedly deoptimize because result didn't fit into Smi.
3037 d2i_instr = new(I) DoubleToIntegerInstr(
3038 new(I) Value(input), call);
3039 } else {
3040 // Optimistically assume result fits into Smi.
3041 d2i_instr = new(I) DoubleToSmiInstr(
3042 new(I) Value(input), call->deopt_id());
3043 }
3044 ReplaceCall(call, d2i_instr);
3045 return true;
3046 }
3047 case MethodRecognizer::kDoubleMod:
3048 case MethodRecognizer::kDoubleRound:
3049 ReplaceWithMathCFunction(call, recognized_kind);
3050 return true;
3051 case MethodRecognizer::kDoubleTruncate:
3052 case MethodRecognizer::kDoubleFloor:
3053 case MethodRecognizer::kDoubleCeil:
3054 if (!TargetCPUFeatures::double_truncate_round_supported()) {
3055 ReplaceWithMathCFunction(call, recognized_kind);
3056 } else {
3057 AddReceiverCheck(call);
3058 DoubleToDoubleInstr* d2d_instr =
3059 new(I) DoubleToDoubleInstr(new(I) Value(call->ArgumentAt(0)),
3060 recognized_kind, call->deopt_id());
3061 ReplaceCall(call, d2d_instr);
3062 }
3063 return true;
3064 case MethodRecognizer::kDoubleAdd:
3065 case MethodRecognizer::kDoubleSub:
3066 case MethodRecognizer::kDoubleMul:
3067 case MethodRecognizer::kDoubleDiv:
3068 return TryReplaceInstanceCallWithInline(call);
3069 default:
3070 // Unsupported method.
3071 return false;
3072 }
3073 }
3074
3075 if (IsSupportedByteArrayViewCid(class_ids[0]) &&
3076 (ic_data.NumberOfChecks() == 1)) {
3077 // For elements that may not fit into a smi on all platforms, check if
3078 // elements fit into a smi or the platform supports unboxed mints.
3079 if ((recognized_kind == MethodRecognizer::kByteArrayBaseGetInt32) ||
3080 (recognized_kind == MethodRecognizer::kByteArrayBaseGetUint32) ||
3081 (recognized_kind == MethodRecognizer::kByteArrayBaseSetInt32) ||
3082 (recognized_kind == MethodRecognizer::kByteArrayBaseSetUint32)) {
3083 if (!CanUnboxInt32()) {
3084 return false;
3085 }
3086 }
3087
3088 if ((recognized_kind == MethodRecognizer::kByteArrayBaseGetFloat32) ||
3089 (recognized_kind == MethodRecognizer::kByteArrayBaseGetFloat64) ||
3090 (recognized_kind == MethodRecognizer::kByteArrayBaseSetFloat32) ||
3091 (recognized_kind == MethodRecognizer::kByteArrayBaseSetFloat64)) {
3092 if (!CanUnboxDouble()) {
3093 return false;
3094 }
3095 }
3096
3097 switch (recognized_kind) {
3098 // ByteArray getters.
3099 case MethodRecognizer::kByteArrayBaseGetInt8:
3100 return BuildByteArrayViewLoad(call, kTypedDataInt8ArrayCid);
3101 case MethodRecognizer::kByteArrayBaseGetUint8:
3102 return BuildByteArrayViewLoad(call, kTypedDataUint8ArrayCid);
3103 case MethodRecognizer::kByteArrayBaseGetInt16:
3104 return BuildByteArrayViewLoad(call, kTypedDataInt16ArrayCid);
3105 case MethodRecognizer::kByteArrayBaseGetUint16:
3106 return BuildByteArrayViewLoad(call, kTypedDataUint16ArrayCid);
3107 case MethodRecognizer::kByteArrayBaseGetInt32:
3108 return BuildByteArrayViewLoad(call, kTypedDataInt32ArrayCid);
3109 case MethodRecognizer::kByteArrayBaseGetUint32:
3110 return BuildByteArrayViewLoad(call, kTypedDataUint32ArrayCid);
3111 case MethodRecognizer::kByteArrayBaseGetFloat32:
3112 return BuildByteArrayViewLoad(call, kTypedDataFloat32ArrayCid);
3113 case MethodRecognizer::kByteArrayBaseGetFloat64:
3114 return BuildByteArrayViewLoad(call, kTypedDataFloat64ArrayCid);
3115 case MethodRecognizer::kByteArrayBaseGetFloat32x4:
3116 return BuildByteArrayViewLoad(call, kTypedDataFloat32x4ArrayCid);
3117 case MethodRecognizer::kByteArrayBaseGetInt32x4:
3118 return BuildByteArrayViewLoad(call, kTypedDataInt32x4ArrayCid);
3119
3120 // ByteArray setters.
3121 case MethodRecognizer::kByteArrayBaseSetInt8:
3122 return BuildByteArrayViewStore(call, kTypedDataInt8ArrayCid);
3123 case MethodRecognizer::kByteArrayBaseSetUint8:
3124 return BuildByteArrayViewStore(call, kTypedDataUint8ArrayCid);
3125 case MethodRecognizer::kByteArrayBaseSetInt16:
3126 return BuildByteArrayViewStore(call, kTypedDataInt16ArrayCid);
3127 case MethodRecognizer::kByteArrayBaseSetUint16:
3128 return BuildByteArrayViewStore(call, kTypedDataUint16ArrayCid);
3129 case MethodRecognizer::kByteArrayBaseSetInt32:
3130 return BuildByteArrayViewStore(call, kTypedDataInt32ArrayCid);
3131 case MethodRecognizer::kByteArrayBaseSetUint32:
3132 return BuildByteArrayViewStore(call, kTypedDataUint32ArrayCid);
3133 case MethodRecognizer::kByteArrayBaseSetFloat32:
3134 return BuildByteArrayViewStore(call, kTypedDataFloat32ArrayCid);
3135 case MethodRecognizer::kByteArrayBaseSetFloat64:
3136 return BuildByteArrayViewStore(call, kTypedDataFloat64ArrayCid);
3137 case MethodRecognizer::kByteArrayBaseSetFloat32x4:
3138 return BuildByteArrayViewStore(call, kTypedDataFloat32x4ArrayCid);
3139 case MethodRecognizer::kByteArrayBaseSetInt32x4:
3140 return BuildByteArrayViewStore(call, kTypedDataInt32x4ArrayCid);
3141 default:
3142 // Unsupported method.
3143 return false;
3144 }
3145 }
3146
3147 if ((class_ids[0] == kFloat32x4Cid) && (ic_data.NumberOfChecks() == 1)) {
3148 return TryInlineFloat32x4Method(call, recognized_kind);
3149 }
3150
3151 if ((class_ids[0] == kInt32x4Cid) && (ic_data.NumberOfChecks() == 1)) {
3152 return TryInlineInt32x4Method(call, recognized_kind);
3153 }
3154
3155 if ((class_ids[0] == kFloat64x2Cid) && (ic_data.NumberOfChecks() == 1)) {
3156 return TryInlineFloat64x2Method(call, recognized_kind);
3157 }
3158
3159 if (recognized_kind == MethodRecognizer::kIntegerLeftShiftWithMask32) {
3160 ASSERT(call->ArgumentCount() == 3);
3161 ASSERT(ic_data.NumArgsTested() == 2);
3162 Definition* value = call->ArgumentAt(0);
3163 Definition* count = call->ArgumentAt(1);
3164 Definition* int32_mask = call->ArgumentAt(2);
3165 if (HasOnlyTwoOf(ic_data, kSmiCid)) {
3166 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
3167 return false;
3168 }
3169 // We cannot overflow. The input value must be a Smi
3170 AddCheckSmi(value, call->deopt_id(), call->env(), call);
3171 AddCheckSmi(count, call->deopt_id(), call->env(), call);
3172 ASSERT(int32_mask->IsConstant());
3173 const Integer& mask_literal = Integer::Cast(
3174 int32_mask->AsConstant()->value());
3175 const int64_t mask_value = mask_literal.AsInt64Value();
3176 ASSERT(mask_value >= 0);
3177 if (mask_value > Smi::kMaxValue) {
3178 // The result will not be Smi.
3179 return false;
3180 }
3181 BinarySmiOpInstr* left_shift =
3182 new(I) BinarySmiOpInstr(Token::kSHL,
3183 new(I) Value(value),
3184 new(I) Value(count),
3185 call->deopt_id());
3186 left_shift->mark_truncating();
3187 if ((kBitsPerWord == 32) && (mask_value == 0xffffffffLL)) {
3188 // No BIT_AND operation needed.
3189 ReplaceCall(call, left_shift);
3190 } else {
3191 InsertBefore(call, left_shift, call->env(), FlowGraph::kValue);
3192 BinarySmiOpInstr* bit_and =
3193 new(I) BinarySmiOpInstr(Token::kBIT_AND,
3194 new(I) Value(left_shift),
3195 new(I) Value(int32_mask),
3196 call->deopt_id());
3197 ReplaceCall(call, bit_and);
3198 }
3199 return true;
3200 }
3201
3202 if (HasTwoMintOrSmi(ic_data) &&
3203 HasOnlyOneSmi(ICData::Handle(I,
3204 ic_data.AsUnaryClassChecksForArgNr(1)))) {
3205 if (!FlowGraphCompiler::SupportsUnboxedMints() ||
3206 ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) {
3207 return false;
3208 }
3209 ShiftMintOpInstr* left_shift =
3210 new(I) ShiftMintOpInstr(Token::kSHL,
3211 new(I) Value(value),
3212 new(I) Value(count),
3213 call->deopt_id());
3214 InsertBefore(call, left_shift, call->env(), FlowGraph::kValue);
3215 BinaryMintOpInstr* bit_and =
3216 new(I) BinaryMintOpInstr(Token::kBIT_AND,
3217 new(I) Value(left_shift),
3218 new(I) Value(int32_mask),
3219 call->deopt_id());
3220 ReplaceCall(call, bit_and);
3221 return true;
3222 }
3223 }
3224 return false;
3225 }
3226
3227
3228 bool FlowGraphOptimizer::TryInlineFloat32x4Constructor(
3229 StaticCallInstr* call,
3230 MethodRecognizer::Kind recognized_kind) {
3231 if (!ShouldInlineSimd()) {
3232 return false;
3233 }
3234 if (recognized_kind == MethodRecognizer::kFloat32x4Zero) {
3235 Float32x4ZeroInstr* zero = new(I) Float32x4ZeroInstr();
3236 ReplaceCall(call, zero);
3237 return true;
3238 } else if (recognized_kind == MethodRecognizer::kFloat32x4Splat) {
3239 Float32x4SplatInstr* splat =
3240 new(I) Float32x4SplatInstr(
3241 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3242 ReplaceCall(call, splat);
3243 return true;
3244 } else if (recognized_kind == MethodRecognizer::kFloat32x4Constructor) {
3245 Float32x4ConstructorInstr* con =
3246 new(I) Float32x4ConstructorInstr(
3247 new(I) Value(call->ArgumentAt(1)),
3248 new(I) Value(call->ArgumentAt(2)),
3249 new(I) Value(call->ArgumentAt(3)),
3250 new(I) Value(call->ArgumentAt(4)),
3251 call->deopt_id());
3252 ReplaceCall(call, con);
3253 return true;
3254 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromInt32x4Bits) {
3255 Int32x4ToFloat32x4Instr* cast =
3256 new(I) Int32x4ToFloat32x4Instr(
3257 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3258 ReplaceCall(call, cast);
3259 return true;
3260 } else if (recognized_kind == MethodRecognizer::kFloat32x4FromFloat64x2) {
3261 Float64x2ToFloat32x4Instr* cast =
3262 new(I) Float64x2ToFloat32x4Instr(
3263 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3264 ReplaceCall(call, cast);
3265 return true;
3266 }
3267 return false;
3268 }
3269
3270
3271 bool FlowGraphOptimizer::TryInlineFloat64x2Constructor(
3272 StaticCallInstr* call,
3273 MethodRecognizer::Kind recognized_kind) {
3274 if (!ShouldInlineSimd()) {
3275 return false;
3276 }
3277 if (recognized_kind == MethodRecognizer::kFloat64x2Zero) {
3278 Float64x2ZeroInstr* zero = new(I) Float64x2ZeroInstr();
3279 ReplaceCall(call, zero);
3280 return true;
3281 } else if (recognized_kind == MethodRecognizer::kFloat64x2Splat) {
3282 Float64x2SplatInstr* splat =
3283 new(I) Float64x2SplatInstr(
3284 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3285 ReplaceCall(call, splat);
3286 return true;
3287 } else if (recognized_kind == MethodRecognizer::kFloat64x2Constructor) {
3288 Float64x2ConstructorInstr* con =
3289 new(I) Float64x2ConstructorInstr(
3290 new(I) Value(call->ArgumentAt(1)),
3291 new(I) Value(call->ArgumentAt(2)),
3292 call->deopt_id());
3293 ReplaceCall(call, con);
3294 return true;
3295 } else if (recognized_kind == MethodRecognizer::kFloat64x2FromFloat32x4) {
3296 Float32x4ToFloat64x2Instr* cast =
3297 new(I) Float32x4ToFloat64x2Instr(
3298 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3299 ReplaceCall(call, cast);
3300 return true;
3301 }
3302 return false;
3303 }
3304
3305
3306 bool FlowGraphOptimizer::TryInlineInt32x4Constructor(
3307 StaticCallInstr* call,
3308 MethodRecognizer::Kind recognized_kind) {
3309 if (!ShouldInlineSimd()) {
3310 return false;
3311 }
3312 if (recognized_kind == MethodRecognizer::kInt32x4BoolConstructor) {
3313 Int32x4BoolConstructorInstr* con =
3314 new(I) Int32x4BoolConstructorInstr(
3315 new(I) Value(call->ArgumentAt(1)),
3316 new(I) Value(call->ArgumentAt(2)),
3317 new(I) Value(call->ArgumentAt(3)),
3318 new(I) Value(call->ArgumentAt(4)),
3319 call->deopt_id());
3320 ReplaceCall(call, con);
3321 return true;
3322 } else if (recognized_kind == MethodRecognizer::kInt32x4FromFloat32x4Bits) {
3323 Float32x4ToInt32x4Instr* cast =
3324 new(I) Float32x4ToInt32x4Instr(
3325 new(I) Value(call->ArgumentAt(1)), call->deopt_id());
3326 ReplaceCall(call, cast);
3327 return true;
3328 } else if (recognized_kind == MethodRecognizer::kInt32x4Constructor) {
3329 Int32x4ConstructorInstr* con =
3330 new(I) Int32x4ConstructorInstr(
3331 new(I) Value(call->ArgumentAt(1)),
3332 new(I) Value(call->ArgumentAt(2)),
3333 new(I) Value(call->ArgumentAt(3)),
3334 new(I) Value(call->ArgumentAt(4)),
3335 call->deopt_id());
3336 ReplaceCall(call, con);
3337 return true;
3338 }
3339 return false;
3340 }
3341
3342
3343 bool FlowGraphOptimizer::TryInlineFloat32x4Method(
3344 InstanceCallInstr* call,
3345 MethodRecognizer::Kind recognized_kind) {
3346 if (!ShouldInlineSimd()) {
3347 return false;
3348 }
3349 ASSERT(call->HasICData());
3350 switch (recognized_kind) {
3351 case MethodRecognizer::kFloat32x4ShuffleX:
3352 case MethodRecognizer::kFloat32x4ShuffleY:
3353 case MethodRecognizer::kFloat32x4ShuffleZ:
3354 case MethodRecognizer::kFloat32x4ShuffleW:
3355 case MethodRecognizer::kFloat32x4GetSignMask:
3356 ASSERT(call->ic_data()->HasReceiverClassId(kFloat32x4Cid));
3357 ASSERT(call->ic_data()->HasOneTarget());
3358 return InlineFloat32x4Getter(call, recognized_kind);
3359
3360 case MethodRecognizer::kFloat32x4Equal:
3361 case MethodRecognizer::kFloat32x4GreaterThan:
3362 case MethodRecognizer::kFloat32x4GreaterThanOrEqual:
3363 case MethodRecognizer::kFloat32x4LessThan:
3364 case MethodRecognizer::kFloat32x4LessThanOrEqual:
3365 case MethodRecognizer::kFloat32x4NotEqual: {
3366 Definition* left = call->ArgumentAt(0);
3367 Definition* right = call->ArgumentAt(1);
3368 // Type check left.
3369 AddCheckClass(left,
3370 ICData::ZoneHandle(
3371 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3372 call->deopt_id(),
3373 call->env(),
3374 call);
3375 // Replace call.
3376 Float32x4ComparisonInstr* cmp =
3377 new(I) Float32x4ComparisonInstr(recognized_kind,
3378 new(I) Value(left),
3379 new(I) Value(right),
3380 call->deopt_id());
3381 ReplaceCall(call, cmp);
3382 return true;
3383 }
3384 case MethodRecognizer::kFloat32x4Min:
3385 case MethodRecognizer::kFloat32x4Max: {
3386 Definition* left = call->ArgumentAt(0);
3387 Definition* right = call->ArgumentAt(1);
3388 // Type check left.
3389 AddCheckClass(left,
3390 ICData::ZoneHandle(
3391 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3392 call->deopt_id(),
3393 call->env(),
3394 call);
3395 Float32x4MinMaxInstr* minmax =
3396 new(I) Float32x4MinMaxInstr(
3397 recognized_kind,
3398 new(I) Value(left),
3399 new(I) Value(right),
3400 call->deopt_id());
3401 ReplaceCall(call, minmax);
3402 return true;
3403 }
3404 case MethodRecognizer::kFloat32x4Scale: {
3405 Definition* left = call->ArgumentAt(0);
3406 Definition* right = call->ArgumentAt(1);
3407 // Type check left.
3408 AddCheckClass(left,
3409 ICData::ZoneHandle(
3410 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3411 call->deopt_id(),
3412 call->env(),
3413 call);
3414 // Left and right values are swapped when handed to the instruction,
3415 // this is done so that the double value is loaded into the output
3416 // register and can be destroyed.
3417 Float32x4ScaleInstr* scale =
3418 new(I) Float32x4ScaleInstr(recognized_kind,
3419 new(I) Value(right),
3420 new(I) Value(left),
3421 call->deopt_id());
3422 ReplaceCall(call, scale);
3423 return true;
3424 }
3425 case MethodRecognizer::kFloat32x4Sqrt:
3426 case MethodRecognizer::kFloat32x4ReciprocalSqrt:
3427 case MethodRecognizer::kFloat32x4Reciprocal: {
3428 Definition* left = call->ArgumentAt(0);
3429 AddCheckClass(left,
3430 ICData::ZoneHandle(
3431 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3432 call->deopt_id(),
3433 call->env(),
3434 call);
3435 Float32x4SqrtInstr* sqrt =
3436 new(I) Float32x4SqrtInstr(recognized_kind,
3437 new(I) Value(left),
3438 call->deopt_id());
3439 ReplaceCall(call, sqrt);
3440 return true;
3441 }
3442 case MethodRecognizer::kFloat32x4WithX:
3443 case MethodRecognizer::kFloat32x4WithY:
3444 case MethodRecognizer::kFloat32x4WithZ:
3445 case MethodRecognizer::kFloat32x4WithW: {
3446 Definition* left = call->ArgumentAt(0);
3447 Definition* right = call->ArgumentAt(1);
3448 // Type check left.
3449 AddCheckClass(left,
3450 ICData::ZoneHandle(
3451 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3452 call->deopt_id(),
3453 call->env(),
3454 call);
3455 Float32x4WithInstr* with = new(I) Float32x4WithInstr(recognized_kind,
3456 new(I) Value(left),
3457 new(I) Value(right),
3458 call->deopt_id());
3459 ReplaceCall(call, with);
3460 return true;
3461 }
3462 case MethodRecognizer::kFloat32x4Absolute:
3463 case MethodRecognizer::kFloat32x4Negate: {
3464 Definition* left = call->ArgumentAt(0);
3465 // Type check left.
3466 AddCheckClass(left,
3467 ICData::ZoneHandle(
3468 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3469 call->deopt_id(),
3470 call->env(),
3471 call);
3472 Float32x4ZeroArgInstr* zeroArg =
3473 new(I) Float32x4ZeroArgInstr(
3474 recognized_kind, new(I) Value(left), call->deopt_id());
3475 ReplaceCall(call, zeroArg);
3476 return true;
3477 }
3478 case MethodRecognizer::kFloat32x4Clamp: {
3479 Definition* left = call->ArgumentAt(0);
3480 Definition* lower = call->ArgumentAt(1);
3481 Definition* upper = call->ArgumentAt(2);
3482 // Type check left.
3483 AddCheckClass(left,
3484 ICData::ZoneHandle(
3485 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3486 call->deopt_id(),
3487 call->env(),
3488 call);
3489 Float32x4ClampInstr* clamp = new(I) Float32x4ClampInstr(
3490 new(I) Value(left),
3491 new(I) Value(lower),
3492 new(I) Value(upper),
3493 call->deopt_id());
3494 ReplaceCall(call, clamp);
3495 return true;
3496 }
3497 case MethodRecognizer::kFloat32x4ShuffleMix:
3498 case MethodRecognizer::kFloat32x4Shuffle: {
3499 return InlineFloat32x4Getter(call, recognized_kind);
3500 }
3501 default:
3502 return false;
3503 }
3504 }
3505
3506
3507 bool FlowGraphOptimizer::TryInlineFloat64x2Method(
3508 InstanceCallInstr* call,
3509 MethodRecognizer::Kind recognized_kind) {
3510 if (!ShouldInlineSimd()) {
3511 return false;
3512 }
3513 ASSERT(call->HasICData());
3514 switch (recognized_kind) {
3515 case MethodRecognizer::kFloat64x2GetX:
3516 case MethodRecognizer::kFloat64x2GetY:
3517 ASSERT(call->ic_data()->HasReceiverClassId(kFloat64x2Cid));
3518 ASSERT(call->ic_data()->HasOneTarget());
3519 return InlineFloat64x2Getter(call, recognized_kind);
3520 case MethodRecognizer::kFloat64x2Negate:
3521 case MethodRecognizer::kFloat64x2Abs:
3522 case MethodRecognizer::kFloat64x2Sqrt:
3523 case MethodRecognizer::kFloat64x2GetSignMask: {
3524 Definition* left = call->ArgumentAt(0);
3525 // Type check left.
3526 AddCheckClass(left,
3527 ICData::ZoneHandle(
3528 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3529 call->deopt_id(),
3530 call->env(),
3531 call);
3532 Float64x2ZeroArgInstr* zeroArg =
3533 new(I) Float64x2ZeroArgInstr(
3534 recognized_kind, new(I) Value(left), call->deopt_id());
3535 ReplaceCall(call, zeroArg);
3536 return true;
3537 }
3538 case MethodRecognizer::kFloat64x2Scale:
3539 case MethodRecognizer::kFloat64x2WithX:
3540 case MethodRecognizer::kFloat64x2WithY:
3541 case MethodRecognizer::kFloat64x2Min:
3542 case MethodRecognizer::kFloat64x2Max: {
3543 Definition* left = call->ArgumentAt(0);
3544 Definition* right = call->ArgumentAt(1);
3545 // Type check left.
3546 AddCheckClass(left,
3547 ICData::ZoneHandle(
3548 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3549 call->deopt_id(),
3550 call->env(),
3551 call);
3552 Float64x2OneArgInstr* zeroArg =
3553 new(I) Float64x2OneArgInstr(recognized_kind,
3554 new(I) Value(left),
3555 new(I) Value(right),
3556 call->deopt_id());
3557 ReplaceCall(call, zeroArg);
3558 return true;
3559 }
3560 default:
3561 return false;
3562 }
3563 }
3564
3565
3566 bool FlowGraphOptimizer::TryInlineInt32x4Method(
3567 InstanceCallInstr* call,
3568 MethodRecognizer::Kind recognized_kind) {
3569 if (!ShouldInlineSimd()) {
3570 return false;
3571 }
3572 ASSERT(call->HasICData());
3573 switch (recognized_kind) {
3574 case MethodRecognizer::kInt32x4ShuffleMix:
3575 case MethodRecognizer::kInt32x4Shuffle:
3576 case MethodRecognizer::kInt32x4GetFlagX:
3577 case MethodRecognizer::kInt32x4GetFlagY:
3578 case MethodRecognizer::kInt32x4GetFlagZ:
3579 case MethodRecognizer::kInt32x4GetFlagW:
3580 case MethodRecognizer::kInt32x4GetSignMask:
3581 ASSERT(call->ic_data()->HasReceiverClassId(kInt32x4Cid));
3582 ASSERT(call->ic_data()->HasOneTarget());
3583 return InlineInt32x4Getter(call, recognized_kind);
3584
3585 case MethodRecognizer::kInt32x4Select: {
3586 Definition* mask = call->ArgumentAt(0);
3587 Definition* trueValue = call->ArgumentAt(1);
3588 Definition* falseValue = call->ArgumentAt(2);
3589 // Type check left.
3590 AddCheckClass(mask,
3591 ICData::ZoneHandle(
3592 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3593 call->deopt_id(),
3594 call->env(),
3595 call);
3596 Int32x4SelectInstr* select = new(I) Int32x4SelectInstr(
3597 new(I) Value(mask),
3598 new(I) Value(trueValue),
3599 new(I) Value(falseValue),
3600 call->deopt_id());
3601 ReplaceCall(call, select);
3602 return true;
3603 }
3604 case MethodRecognizer::kInt32x4WithFlagX:
3605 case MethodRecognizer::kInt32x4WithFlagY:
3606 case MethodRecognizer::kInt32x4WithFlagZ:
3607 case MethodRecognizer::kInt32x4WithFlagW: {
3608 Definition* left = call->ArgumentAt(0);
3609 Definition* flag = call->ArgumentAt(1);
3610 // Type check left.
3611 AddCheckClass(left,
3612 ICData::ZoneHandle(
3613 I, call->ic_data()->AsUnaryClassChecksForArgNr(0)),
3614 call->deopt_id(),
3615 call->env(),
3616 call);
3617 Int32x4SetFlagInstr* setFlag = new(I) Int32x4SetFlagInstr(
3618 recognized_kind,
3619 new(I) Value(left),
3620 new(I) Value(flag),
3621 call->deopt_id());
3622 ReplaceCall(call, setFlag);
3623 return true;
3624 }
3625 default:
3626 return false;
3627 }
3628 }
3629
3630
3631 bool FlowGraphOptimizer::InlineByteArrayViewLoad(Instruction* call,
3632 Definition* receiver,
3633 intptr_t array_cid,
3634 intptr_t view_cid,
3635 const ICData& ic_data,
3636 TargetEntryInstr** entry,
3637 Definition** last) {
3638 ASSERT(array_cid != kIllegalCid);
3639 Definition* array = receiver;
3640 Definition* index = call->ArgumentAt(1);
3641 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
3642 call->GetBlock()->try_index());
3643 (*entry)->InheritDeoptTarget(I, call);
3644 Instruction* cursor = *entry;
3645
3646 array_cid = PrepareInlineByteArrayViewOp(call,
3647 array_cid,
3648 view_cid,
3649 &array,
3650 index,
3651 &cursor);
3652
3653 intptr_t deopt_id = Isolate::kNoDeoptId;
3654 if ((array_cid == kTypedDataInt32ArrayCid) ||
3655 (array_cid == kTypedDataUint32ArrayCid)) {
3656 // Deoptimization may be needed if result does not always fit in a Smi.
3657 deopt_id = (kSmiBits >= 32) ? Isolate::kNoDeoptId : call->deopt_id();
3658 }
3659
3660 *last = new(I) LoadIndexedInstr(new(I) Value(array),
3661 new(I) Value(index),
3662 1,
3663 view_cid,
3664 deopt_id,
3665 call->token_pos());
3666 cursor = flow_graph()->AppendTo(
3667 cursor,
3668 *last,
3669 deopt_id != Isolate::kNoDeoptId ? call->env() : NULL,
3670 FlowGraph::kValue);
3671
3672 if (view_cid == kTypedDataFloat32ArrayCid) {
3673 *last = new(I) FloatToDoubleInstr(new(I) Value(*last), deopt_id);
3674 flow_graph()->AppendTo(cursor,
3675 *last,
3676 deopt_id != Isolate::kNoDeoptId ? call->env() : NULL,
3677 FlowGraph::kValue);
3678 }
3679 return true;
3680 }
3681
3682
3683 bool FlowGraphOptimizer::InlineByteArrayViewStore(const Function& target,
3684 Instruction* call,
3685 Definition* receiver,
3686 intptr_t array_cid,
3687 intptr_t view_cid,
3688 const ICData& ic_data,
3689 TargetEntryInstr** entry,
3690 Definition** last) {
3691 ASSERT(array_cid != kIllegalCid);
3692 Definition* array = receiver;
3693 Definition* index = call->ArgumentAt(1);
3694 *entry = new(I) TargetEntryInstr(flow_graph()->allocate_block_id(),
3695 call->GetBlock()->try_index());
3696 (*entry)->InheritDeoptTarget(I, call);
3697 Instruction* cursor = *entry;
3698
3699 array_cid = PrepareInlineByteArrayViewOp(call,
3700 array_cid,
3701 view_cid,
3702 &array,
3703 index,
3704 &cursor);
3705
3706 // Extract the instance call so we can use the function_name in the stored
3707 // value check ICData.
3708 InstanceCallInstr* i_call = NULL;
3709 if (call->IsPolymorphicInstanceCall()) {
3710 i_call = call->AsPolymorphicInstanceCall()->instance_call();
3711 } else {
3712 ASSERT(call->IsInstanceCall());
3713 i_call = call->AsInstanceCall();
3714 }
3715 ASSERT(i_call != NULL);
3716 ICData& value_check = ICData::ZoneHandle(I);
3717 switch (view_cid) {
3718 case kTypedDataInt8ArrayCid:
3719 case kTypedDataUint8ArrayCid:
3720 case kTypedDataUint8ClampedArrayCid:
3721 case kExternalTypedDataUint8ArrayCid:
3722 case kExternalTypedDataUint8ClampedArrayCid:
3723 case kTypedDataInt16ArrayCid:
3724 case kTypedDataUint16ArrayCid: {
3725 // Check that value is always smi.
3726 value_check = ICData::New(flow_graph_->parsed_function()->function(),
3727 i_call->function_name(),
3728 Object::empty_array(), // Dummy args. descr.
3729 Isolate::kNoDeoptId,
3730 1);
3731 value_check.AddReceiverCheck(kSmiCid, target);
3732 break;
3733 }
3734 case kTypedDataInt32ArrayCid:
3735 case kTypedDataUint32ArrayCid:
3736 // On 64-bit platforms assume that stored value is always a smi.
3737 if (kSmiBits >= 32) {
3738 value_check = ICData::New(flow_graph_->parsed_function()->function(),
3739 i_call->function_name(),
3740 Object::empty_array(), // Dummy args. descr.
3741 Isolate::kNoDeoptId,
3742 1);
3743 value_check.AddReceiverCheck(kSmiCid, target);
3744 }
3745 break;
3746 case kTypedDataFloat32ArrayCid:
3747 case kTypedDataFloat64ArrayCid: {
3748 // Check that value is always double.
3749 value_check = ICData::New(flow_graph_->parsed_function()->function(),
3750 i_call->function_name(),
3751 Object::empty_array(), // Dummy args. descr.
3752 Isolate::kNoDeoptId,
3753 1);
3754 value_check.AddReceiverCheck(kDoubleCid, target);
3755 break;
3756 }
3757 case kTypedDataInt32x4ArrayCid: {
3758 // Check that value is always Int32x4.
3759 value_check = ICData::New(flow_graph_->parsed_function()->function(),
3760 i_call->function_name(),
3761 Object::empty_array(), // Dummy args. descr.
3762 Isolate::kNoDeoptId,
3763 1);
3764 value_check.AddReceiverCheck(kInt32x4Cid, target);
3765 break;
3766 }
3767 case kTypedDataFloat32x4ArrayCid: {
3768 // Check that value is always Float32x4.
3769 value_check = ICData::New(flow_graph_->parsed_function()->function(),
3770 i_call->function_name(),
3771 Object::empty_array(), // Dummy args. descr.
3772 Isolate::kNoDeoptId,
3773 1);
3774 value_check.AddReceiverCheck(kFloat32x4Cid, target);
3775 break;
3776 }
3777 default:
3778 // Array cids are already checked in the caller.
3779 UNREACHABLE();
3780 }
3781
3782 Definition* stored_value = call->ArgumentAt(2);
3783 if (!value_check.IsNull()) {
3784 AddCheckClass(stored_value, value_check, call->deopt_id(), call->env(),
3785 call);
3786 }
3787
3788 if (view_cid == kTypedDataFloat32ArrayCid) {
3789 stored_value = new(I) DoubleToFloatInstr(
3790 new(I) Value(stored_value), call->deopt_id());
3791 cursor = flow_graph()->AppendTo(cursor,
3792 stored_value,
3793 NULL,
3794 FlowGraph::kValue);
3795 } else if (view_cid == kTypedDataInt32ArrayCid) {
3796 stored_value = new(I) UnboxInt32Instr(
3797 UnboxInt32Instr::kTruncate,
3798 new(I) Value(stored_value),
3799 call->deopt_id());
3800 cursor = flow_graph()->AppendTo(cursor,
3801 stored_value,
3802 call->env(),
3803 FlowGraph::kValue);
3804 } else if (view_cid == kTypedDataUint32ArrayCid) {
3805 stored_value = new(I) UnboxUint32Instr(
3806 new(I) Value(stored_value),
3807 call->deopt_id());
3808 ASSERT(stored_value->AsUnboxInteger()->is_truncating());
3809 cursor = flow_graph()->AppendTo(cursor,
3810 stored_value,
3811 call->env(),
3812 FlowGraph::kValue);
3813 }
3814
3815 StoreBarrierType needs_store_barrier = kNoStoreBarrier;
3816 *last = new(I) StoreIndexedInstr(new(I) Value(array),
3817 new(I) Value(index),
3818 new(I) Value(stored_value),
3819 needs_store_barrier,
3820 1, // Index scale
3821 view_cid,
3822 call->deopt_id(),
3823 call->token_pos());
3824
3825 flow_graph()->AppendTo(cursor,
3826 *last,
3827 call->deopt_id() != Isolate::kNoDeoptId ?
3828 call->env() : NULL,
3829 FlowGraph::kEffect);
3830 return true;
3831 }
3832
3833
3834
3835 intptr_t FlowGraphOptimizer::PrepareInlineByteArrayViewOp(
3836 Instruction* call,
3837 intptr_t array_cid,
3838 intptr_t view_cid,
3839 Definition** array,
3840 Definition* byte_index,
3841 Instruction** cursor) {
3842 // Insert byte_index smi check.
3843 *cursor = flow_graph()->AppendTo(*cursor,
3844 new(I) CheckSmiInstr(
3845 new(I) Value(byte_index),
3846 call->deopt_id(),
3847 call->token_pos()),
3848 call->env(),
3849 FlowGraph::kEffect);
3850
3851 LoadFieldInstr* length =
3852 new(I) LoadFieldInstr(
3853 new(I) Value(*array),
3854 CheckArrayBoundInstr::LengthOffsetFor(array_cid),
3855 Type::ZoneHandle(I, Type::SmiType()),
3856 call->token_pos());
3857 length->set_is_immutable(true);
3858 length->set_result_cid(kSmiCid);
3859 length->set_recognized_kind(
3860 LoadFieldInstr::RecognizedKindFromArrayCid(array_cid));
3861 *cursor = flow_graph()->AppendTo(*cursor,
3862 length,
3863 NULL,
3864 FlowGraph::kValue);
3865
3866 intptr_t element_size = Instance::ElementSizeFor(array_cid);
3867 ConstantInstr* bytes_per_element =
3868 flow_graph()->GetConstant(Smi::Handle(I, Smi::New(element_size)));
3869 BinarySmiOpInstr* len_in_bytes =
3870 new(I) BinarySmiOpInstr(Token::kMUL,
3871 new(I) Value(length),
3872 new(I) Value(bytes_per_element),
3873 call->deopt_id());
3874 *cursor = flow_graph()->AppendTo(*cursor, len_in_bytes, call->env(),
3875 FlowGraph::kValue);
3876
3877 // adjusted_length = len_in_bytes - (element_size - 1).
3878 Definition* adjusted_length = len_in_bytes;
3879 intptr_t adjustment = Instance::ElementSizeFor(view_cid) - 1;
3880 if (adjustment > 0) {
3881 ConstantInstr* length_adjustment =
3882 flow_graph()->GetConstant(Smi::Handle(I, Smi::New(adjustment)));
3883 adjusted_length =
3884 new(I) BinarySmiOpInstr(Token::kSUB,
3885 new(I) Value(len_in_bytes),
3886 new(I) Value(length_adjustment),
3887 call->deopt_id());
3888 *cursor = flow_graph()->AppendTo(*cursor, adjusted_length, call->env(),
3889 FlowGraph::kValue);
3890 }
3891
3892 // Check adjusted_length > 0.
3893 ConstantInstr* zero =
3894 flow_graph()->GetConstant(Smi::Handle(I, Smi::New(0)));
3895 *cursor = flow_graph()->AppendTo(*cursor,
3896 new(I) CheckArrayBoundInstr(
3897 new(I) Value(adjusted_length),
3898 new(I) Value(zero),
3899 call->deopt_id()),
3900 call->env(),
3901 FlowGraph::kEffect);
3902 // Check 0 <= byte_index < adjusted_length.
3903 *cursor = flow_graph()->AppendTo(*cursor,
3904 new(I) CheckArrayBoundInstr(
3905 new(I) Value(adjusted_length),
3906 new(I) Value(byte_index),
3907 call->deopt_id()),
3908 call->env(),
3909 FlowGraph::kEffect);
3910
3911 if (RawObject::IsExternalTypedDataClassId(array_cid)) {
3912 LoadUntaggedInstr* elements =
3913 new(I) LoadUntaggedInstr(new(I) Value(*array),
3914 ExternalTypedData::data_offset());
3915 *cursor = flow_graph()->AppendTo(*cursor,
3916 elements,
3917 NULL,
3918 FlowGraph::kValue);
3919 *array = elements;
3920 }
3921 return array_cid;
3922 }
3923
3924
3925 bool FlowGraphOptimizer::BuildByteArrayViewLoad(InstanceCallInstr* call,
3926 intptr_t view_cid) {
3927 const bool simd_view = (view_cid == kTypedDataFloat32x4ArrayCid) ||
3928 (view_cid == kTypedDataInt32x4ArrayCid);
3929 const bool float_view = (view_cid == kTypedDataFloat32ArrayCid) ||
3930 (view_cid == kTypedDataFloat64ArrayCid);
3931 if (float_view && !CanUnboxDouble()) {
3932 return false;
3933 }
3934 if (simd_view && !ShouldInlineSimd()) {
3935 return false;
3936 }
3937 return TryReplaceInstanceCallWithInline(call);
3938 }
3939
3940
3941 bool FlowGraphOptimizer::BuildByteArrayViewStore(InstanceCallInstr* call,
3942 intptr_t view_cid) {
3943 const bool simd_view = (view_cid == kTypedDataFloat32x4ArrayCid) ||
3944 (view_cid == kTypedDataInt32x4ArrayCid);
3945 const bool float_view = (view_cid == kTypedDataFloat32ArrayCid) ||
3946 (view_cid == kTypedDataFloat64ArrayCid);
3947 if (float_view && !CanUnboxDouble()) {
3948 return false;
3949 }
3950 if (simd_view && !ShouldInlineSimd()) {
3951 return false;
3952 }
3953 return TryReplaceInstanceCallWithInline(call);
3954 }
3955
3956
3957 // If type tests specified by 'ic_data' do not depend on type arguments,
3958 // return mapping cid->result in 'results' (i : cid; i + 1: result).
3959 // If all tests yield the same result, return it otherwise return Bool::null.
3960 // If no mapping is possible, 'results' is empty.
3961 // An instance-of test returning all same results can be converted to a class
3962 // check.
3963 RawBool* FlowGraphOptimizer::InstanceOfAsBool(
3964 const ICData& ic_data,
3965 const AbstractType& type,
3966 ZoneGrowableArray<intptr_t>* results) const {
3967 ASSERT(results->is_empty());
3968 ASSERT(ic_data.NumArgsTested() == 1); // Unary checks only.
3969 if (!type.IsInstantiated() || type.IsMalformedOrMalbounded()) {
3970 return Bool::null();
3971 }
3972 const Class& type_class = Class::Handle(I, type.type_class());
3973 const intptr_t num_type_args = type_class.NumTypeArguments();
3974 if (num_type_args > 0) {
3975 // Only raw types can be directly compared, thus disregarding type
3976 // arguments.
3977 const intptr_t num_type_params = type_class.NumTypeParameters();
3978 const intptr_t from_index = num_type_args - num_type_params;
3979 const TypeArguments& type_arguments =
3980 TypeArguments::Handle(I, type.arguments());
3981 const bool is_raw_type = type_arguments.IsNull() ||
3982 type_arguments.IsRaw(from_index, num_type_params);
3983 if (!is_raw_type) {
3984 // Unknown result.
3985 return Bool::null();
3986 }
3987 }
3988
3989 const ClassTable& class_table = *isolate()->class_table();
3990 Bool& prev = Bool::Handle(I);
3991 Class& cls = Class::Handle(I);
3992
3993 bool results_differ = false;
3994 for (int i = 0; i < ic_data.NumberOfChecks(); i++) {
3995 cls = class_table.At(ic_data.GetReceiverClassIdAt(i));
3996 if (cls.NumTypeArguments() > 0) {
3997 return Bool::null();
3998 }
3999 const bool is_subtype = cls.IsSubtypeOf(
4000 TypeArguments::Handle(I),
4001 type_class,
4002 TypeArguments::Handle(I),
4003 NULL);
4004 results->Add(cls.id());
4005 results->Add(is_subtype);
4006 if (prev.IsNull()) {
4007 prev = Bool::Get(is_subtype).raw();
4008 } else {
4009 if (is_subtype != prev.value()) {
4010 results_differ = true;
4011 }
4012 }
4013 }
4014 return results_differ ? Bool::null() : prev.raw();
4015 }
4016
4017
4018 // Returns true if checking against this type is a direct class id comparison.
4019 bool FlowGraphOptimizer::TypeCheckAsClassEquality(const AbstractType& type) {
4020 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
4021 // Requires CHA.
4022 if (!FLAG_use_cha) return false;
4023 if (!type.IsInstantiated()) return false;
4024 const Class& type_class = Class::Handle(type.type_class());
4025 // Signature classes have different type checking rules.
4026 if (type_class.IsSignatureClass()) return false;
4027 // Could be an interface check?
4028 if (isolate()->cha()->IsImplemented(type_class)) return false;
4029 // Check if there are subclasses.
4030 if (isolate()->cha()->HasSubclasses(type_class)) return false;
4031 const intptr_t num_type_args = type_class.NumTypeArguments();
4032 if (num_type_args > 0) {
4033 // Only raw types can be directly compared, thus disregarding type
4034 // arguments.
4035 const intptr_t num_type_params = type_class.NumTypeParameters();
4036 const intptr_t from_index = num_type_args - num_type_params;
4037 const TypeArguments& type_arguments =
4038 TypeArguments::Handle(type.arguments());
4039 const bool is_raw_type = type_arguments.IsNull() ||
4040 type_arguments.IsRaw(from_index, num_type_params);
4041 return is_raw_type;
4042 }
4043 return true;
4044 }
4045
4046
4047 static bool CidTestResultsContains(const ZoneGrowableArray<intptr_t>& results,
4048 intptr_t test_cid) {
4049 for (intptr_t i = 0; i < results.length(); i += 2) {
4050 if (results[i] == test_cid) return true;
4051 }
4052 return false;
4053 }
4054
4055
4056 static void TryAddTest(ZoneGrowableArray<intptr_t>* results,
4057 intptr_t test_cid,
4058 bool result) {
4059 if (!CidTestResultsContains(*results, test_cid)) {
4060 results->Add(test_cid);
4061 results->Add(result);
4062 }
4063 }
4064
4065
4066 // Tries to add cid tests to 'results' so that no deoptimization is
4067 // necessary.
4068 // TODO(srdjan): Do also for other than 'int' type.
4069 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results,
4070 const AbstractType& type) {
4071 ASSERT(results->length() >= 2); // At least on eentry.
4072 const ClassTable& class_table = *Isolate::Current()->class_table();
4073 if ((*results)[0] != kSmiCid) {
4074 const Class& cls = Class::Handle(class_table.At(kSmiCid));
4075 const Class& type_class = Class::Handle(type.type_class());
4076 const bool smi_is_subtype = cls.IsSubtypeOf(TypeArguments::Handle(),
4077 type_class,
4078 TypeArguments::Handle(),
4079 NULL);
4080 results->Add((*results)[results->length() - 2]);
4081 results->Add((*results)[results->length() - 2]);
4082 for (intptr_t i = results->length() - 3; i > 1; --i) {
4083 (*results)[i] = (*results)[i - 2];
4084 }
4085 (*results)[0] = kSmiCid;
4086 (*results)[1] = smi_is_subtype;
4087 }
4088
4089 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded());
4090 ASSERT(results->length() >= 2);
4091 if (type.IsIntType()) {
4092 ASSERT((*results)[0] == kSmiCid);
4093 TryAddTest(results, kMintCid, true);
4094 TryAddTest(results, kBigintCid, true);
4095 // Cannot deoptimize since all tests returning true have been added.
4096 return false;
4097 }
4098
4099 return true; // May deoptimize since we have not identified all 'true' tests.
4100 }
4101
4102
4103 // TODO(srdjan): Use ICData to check if always true or false.
4104 void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
4105 ASSERT(Token::IsTypeTestOperator(call->token_kind()));
4106 Definition* left = call->ArgumentAt(0);
4107 Definition* instantiator = call->ArgumentAt(1);
4108 Definition* type_args = call->ArgumentAt(2);
4109 const AbstractType& type =
4110 AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value());
4111 const bool negate = Bool::Cast(
4112 call->ArgumentAt(4)->OriginalDefinition()->AsConstant()->value()).value();
4113 const ICData& unary_checks =
4114 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecks());
4115 if (FLAG_warn_on_javascript_compatibility &&
4116 !unary_checks.IssuedJSWarning() &&
4117 (type.IsIntType() || type.IsDoubleType() || !type.IsInstantiated())) {
4118 // No warning was reported yet for this type check, either because it has
4119 // not been executed yet, or because no problematic combinations of instance
4120 // type and test type have been encountered so far. A warning may still be
4121 // reported, so do not replace the instance call.
4122 return;
4123 }
4124 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
4125 ZoneGrowableArray<intptr_t>* results =
4126 new(I) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
4127 Bool& as_bool =
4128 Bool::ZoneHandle(I, InstanceOfAsBool(unary_checks, type, results));
4129 if (as_bool.IsNull()) {
4130 if (results->length() == unary_checks.NumberOfChecks() * 2) {
4131 const bool can_deopt = TryExpandTestCidsResult(results, type);
4132 TestCidsInstr* test_cids = new(I) TestCidsInstr(
4133 call->token_pos(),
4134 negate ? Token::kISNOT : Token::kIS,
4135 new(I) Value(left),
4136 *results,
4137 can_deopt ? call->deopt_id() : Isolate::kNoDeoptId);
4138 // Remove type.
4139 ReplaceCall(call, test_cids);
4140 return;
4141 }
4142 } else {
4143 // TODO(srdjan): Use TestCidsInstr also for this case.
4144 // One result only.
4145 AddReceiverCheck(call);
4146 if (negate) {
4147 as_bool = Bool::Get(!as_bool.value()).raw();
4148 }
4149 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool);
4150 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
4151 PushArgumentInstr* push = call->PushArgumentAt(i);
4152 push->ReplaceUsesWith(push->value()->definition());
4153 push->RemoveFromGraph();
4154 }
4155 call->ReplaceUsesWith(bool_const);
4156 ASSERT(current_iterator()->Current() == call);
4157 current_iterator()->RemoveCurrentFromGraph();
4158 return;
4159 }
4160 }
4161
4162 if (TypeCheckAsClassEquality(type)) {
4163 LoadClassIdInstr* left_cid = new(I) LoadClassIdInstr(new(I) Value(left));
4164 InsertBefore(call,
4165 left_cid,
4166 NULL,
4167 FlowGraph::kValue);
4168 const intptr_t type_cid = Class::Handle(I, type.type_class()).id();
4169 ConstantInstr* cid =
4170 flow_graph()->GetConstant(Smi::Handle(I, Smi::New(type_cid)));
4171
4172 StrictCompareInstr* check_cid =
4173 new(I) StrictCompareInstr(
4174 call->token_pos(),
4175 negate ? Token::kNE_STRICT : Token::kEQ_STRICT,
4176 new(I) Value(left_cid),
4177 new(I) Value(cid),
4178 false); // No number check.
4179 ReplaceCall(call, check_cid);
4180 return;
4181 }
4182
4183 InstanceOfInstr* instance_of =
4184 new(I) InstanceOfInstr(call->token_pos(),
4185 new(I) Value(left),
4186 new(I) Value(instantiator),
4187 new(I) Value(type_args),
4188 type,
4189 negate,
4190 call->deopt_id());
4191 ReplaceCall(call, instance_of);
4192 }
4193
4194
4195 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids).
4196 void FlowGraphOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) {
4197 ASSERT(Token::IsTypeCastOperator(call->token_kind()));
4198 Definition* left = call->ArgumentAt(0);
4199 Definition* instantiator = call->ArgumentAt(1);
4200 Definition* type_args = call->ArgumentAt(2);
4201 const AbstractType& type =
4202 AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value());
4203 ASSERT(!type.IsMalformedOrMalbounded());
4204 const ICData& unary_checks =
4205 ICData::ZoneHandle(I, call->ic_data()->AsUnaryClassChecks());
4206 if (FLAG_warn_on_javascript_compatibility &&
4207 !unary_checks.IssuedJSWarning() &&
4208 (type.IsIntType() || type.IsDoubleType() || !type.IsInstantiated())) {
4209 // No warning was reported yet for this type check, either because it has
4210 // not been executed yet, or because no problematic combinations of instance
4211 // type and test type have been encountered so far. A warning may still be
4212 // reported, so do not replace the instance call.
4213 return;
4214 }
4215 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
4216 ZoneGrowableArray<intptr_t>* results =
4217 new(I) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2);
4218 const Bool& as_bool = Bool::ZoneHandle(I,
4219 InstanceOfAsBool(unary_checks, type, results));
4220 if (as_bool.raw() == Bool::True().raw()) {
4221 AddReceiverCheck(call);
4222 // Remove the original push arguments.
4223 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
4224 PushArgumentInstr* push = call->PushArgumentAt(i);
4225 push->ReplaceUsesWith(push->value()->definition());
4226 push->RemoveFromGraph();
4227 }
4228 // Remove call, replace it with 'left'.
4229 call->ReplaceUsesWith(left);
4230 ASSERT(current_iterator()->Current() == call);
4231 current_iterator()->RemoveCurrentFromGraph();
4232 return;
4233 }
4234 }
4235 const String& dst_name = String::ZoneHandle(I,
4236 Symbols::New(Exceptions::kCastErrorDstName));
4237 AssertAssignableInstr* assert_as =
4238 new(I) AssertAssignableInstr(call->token_pos(),
4239 new(I) Value(left),
4240 new(I) Value(instantiator),
4241 new(I) Value(type_args),
4242 type,
4243 dst_name,
4244 call->deopt_id());
4245 ReplaceCall(call, assert_as);
4246 }
4247
4248
4249 // Tries to optimize instance call by replacing it with a faster instruction
4250 // (e.g, binary op, field load, ..).
4251 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) {
4252 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) {
4253 return;
4254 }
4255
4256 const Token::Kind op_kind = instr->token_kind();
4257 // Type test is special as it always gets converted into inlined code.
4258 if (Token::IsTypeTestOperator(op_kind)) {
4259 ReplaceWithInstanceOf(instr);
4260 return;
4261 }
4262
4263 if (Token::IsTypeCastOperator(op_kind)) {
4264 ReplaceWithTypeCast(instr);
4265 return;
4266 }
4267
4268 const ICData& unary_checks =
4269 ICData::ZoneHandle(I, instr->ic_data()->AsUnaryClassChecks());
4270
4271 const intptr_t max_checks = (op_kind == Token::kEQ)
4272 ? FLAG_max_equality_polymorphic_checks
4273 : FLAG_max_polymorphic_checks;
4274 if ((unary_checks.NumberOfChecks() > max_checks) &&
4275 InstanceCallNeedsClassCheck(instr, RawFunction::kRegularFunction)) {
4276 // Too many checks, it will be megamorphic which needs unary checks.
4277 instr->set_ic_data(&unary_checks);
4278 return;
4279 }
4280
4281 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithStoreIndexed(instr)) {
4282 return;
4283 }
4284 if ((op_kind == Token::kINDEX) && TryReplaceWithLoadIndexed(instr)) {
4285 return;
4286 }
4287
4288 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) {
4289 return;
4290 }
4291
4292 if (Token::IsRelationalOperator(op_kind) &&
4293 TryReplaceWithRelationalOp(instr, op_kind)) {
4294 return;
4295 }
4296
4297 if (Token::IsBinaryOperator(op_kind) &&
4298 TryReplaceWithBinaryOp(instr, op_kind)) {
4299 return;
4300 }
4301 if (Token::IsUnaryOperator(op_kind) &&
4302 TryReplaceWithUnaryOp(instr, op_kind)) {
4303 return;
4304 }
4305 if ((op_kind == Token::kGET) && TryInlineInstanceGetter(instr)) {
4306 return;
4307 }
4308 if ((op_kind == Token::kSET) &&
4309 TryInlineInstanceSetter(instr, unary_checks)) {
4310 return;
4311 }
4312 if (TryInlineInstanceMethod(instr)) {
4313 return;
4314 }
4315
4316 bool has_one_target = unary_checks.HasOneTarget();
4317
4318 if (has_one_target) {
4319 // Check if the single target is a polymorphic target, if it is,
4320 // we don't have one target.
4321 const Function& target =
4322 Function::Handle(I, unary_checks.GetTargetAt(0));
4323 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target);
4324 has_one_target = !polymorphic_target;
4325 }
4326
4327 if (has_one_target) {
4328 RawFunction::Kind function_kind =
4329 Function::Handle(I, unary_checks.GetTargetAt(0)).kind();
4330 if (!InstanceCallNeedsClassCheck(instr, function_kind)) {
4331 const bool call_with_checks = false;
4332 PolymorphicInstanceCallInstr* call =
4333 new(I) PolymorphicInstanceCallInstr(instr, unary_checks,
4334 call_with_checks);
4335 instr->ReplaceWith(call, current_iterator());
4336 return;
4337 }
4338 }
4339
4340 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) {
4341 bool call_with_checks;
4342 if (has_one_target) {
4343 // Type propagation has not run yet, we cannot eliminate the check.
4344 AddReceiverCheck(instr);
4345 // Call can still deoptimize, do not detach environment from instr.
4346 call_with_checks = false;
4347 } else {
4348 call_with_checks = true;
4349 }
4350 PolymorphicInstanceCallInstr* call =
4351 new(I) PolymorphicInstanceCallInstr(instr, unary_checks,
4352 call_with_checks);
4353 instr->ReplaceWith(call, current_iterator());
4354 }
4355 }
4356
4357
4358 void FlowGraphOptimizer::VisitStaticCall(StaticCallInstr* call) {
4359 if (!CanUnboxDouble()) {
4360 return;
4361 }
4362 MethodRecognizer::Kind recognized_kind =
4363 MethodRecognizer::RecognizeKind(call->function());
4364 MathUnaryInstr::MathUnaryKind unary_kind;
4365 switch (recognized_kind) {
4366 case MethodRecognizer::kMathSqrt:
4367 unary_kind = MathUnaryInstr::kSqrt;
4368 break;
4369 case MethodRecognizer::kMathSin:
4370 unary_kind = MathUnaryInstr::kSin;
4371 break;
4372 case MethodRecognizer::kMathCos:
4373 unary_kind = MathUnaryInstr::kCos;
4374 break;
4375 default:
4376 unary_kind = MathUnaryInstr::kIllegal;
4377 break;
4378 }
4379 if (unary_kind != MathUnaryInstr::kIllegal) {
4380 MathUnaryInstr* math_unary =
4381 new(I) MathUnaryInstr(unary_kind,
4382 new(I) Value(call->ArgumentAt(0)),
4383 call->deopt_id());
4384 ReplaceCall(call, math_unary);
4385 } else if ((recognized_kind == MethodRecognizer::kFloat32x4Zero) ||
4386 (recognized_kind == MethodRecognizer::kFloat32x4Splat) ||
4387 (recognized_kind == MethodRecognizer::kFloat32x4Constructor) ||
4388 (recognized_kind == MethodRecognizer::kFloat32x4FromFloat64x2)) {
4389 TryInlineFloat32x4Constructor(call, recognized_kind);
4390 } else if ((recognized_kind == MethodRecognizer::kFloat64x2Constructor) ||
4391 (recognized_kind == MethodRecognizer::kFloat64x2Zero) ||
4392 (recognized_kind == MethodRecognizer::kFloat64x2Splat) ||
4393 (recognized_kind == MethodRecognizer::kFloat64x2FromFloat32x4)) {
4394 TryInlineFloat64x2Constructor(call, recognized_kind);
4395 } else if ((recognized_kind == MethodRecognizer::kInt32x4BoolConstructor) ||
4396 (recognized_kind == MethodRecognizer::kInt32x4Constructor)) {
4397 TryInlineInt32x4Constructor(call, recognized_kind);
4398 } else if (recognized_kind == MethodRecognizer::kObjectConstructor) {
4399 // Remove the original push arguments.
4400 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
4401 PushArgumentInstr* push = call->PushArgumentAt(i);
4402 push->ReplaceUsesWith(push->value()->definition());
4403 push->RemoveFromGraph();
4404 }
4405 // Manually replace call with global null constant. ReplaceCall can't
4406 // be used for definitions that are already in the graph.
4407 call->ReplaceUsesWith(flow_graph_->constant_null());
4408 ASSERT(current_iterator()->Current() == call);
4409 current_iterator()->RemoveCurrentFromGraph();;
4410 } else if ((recognized_kind == MethodRecognizer::kMathMin) ||
4411 (recognized_kind == MethodRecognizer::kMathMax)) {
4412 // We can handle only monomorphic min/max call sites with both arguments
4413 // being either doubles or smis.
4414 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) {
4415 const ICData& ic_data = *call->ic_data();
4416 intptr_t result_cid = kIllegalCid;
4417 if (ICDataHasReceiverArgumentClassIds(ic_data, kDoubleCid, kDoubleCid)) {
4418 result_cid = kDoubleCid;
4419 } else if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) {
4420 result_cid = kSmiCid;
4421 }
4422 if (result_cid != kIllegalCid) {
4423 MathMinMaxInstr* min_max = new(I) MathMinMaxInstr(
4424 recognized_kind,
4425 new(I) Value(call->ArgumentAt(0)),
4426 new(I) Value(call->ArgumentAt(1)),
4427 call->deopt_id(),
4428 result_cid);
4429 const ICData& unary_checks =
4430 ICData::ZoneHandle(I, ic_data.AsUnaryClassChecks());
4431 AddCheckClass(min_max->left()->definition(),
4432 unary_checks,
4433 call->deopt_id(),
4434 call->env(),
4435 call);
4436 AddCheckClass(min_max->right()->definition(),
4437 unary_checks,
4438 call->deopt_id(),
4439 call->env(),
4440 call);
4441 ReplaceCall(call, min_max);
4442 }
4443 }
4444 } else if (recognized_kind == MethodRecognizer::kMathDoublePow) {
4445 // We know that first argument is double, the second is num.
4446 // InvokeMathCFunctionInstr requires unboxed doubles. UnboxDouble
4447 // instructions contain type checks and conversions to double.
4448 ZoneGrowableArray<Value*>* args =
4449 new(I) ZoneGrowableArray<Value*>(call->ArgumentCount());
4450 for (intptr_t i = 0; i < call->ArgumentCount(); i++) {
4451 args->Add(new(I) Value(call->ArgumentAt(i)));
4452 }
4453 InvokeMathCFunctionInstr* invoke =
4454 new(I) InvokeMathCFunctionInstr(args,
4455 call->deopt_id(),
4456 recognized_kind,
4457 call->token_pos());
4458 ReplaceCall(call, invoke);
4459 } else if (recognized_kind == MethodRecognizer::kDoubleFromInteger) {
4460 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) {
4461 const ICData& ic_data = *call->ic_data();
4462 if (CanUnboxDouble()) {
4463 if (ArgIsAlways(kSmiCid, ic_data, 1)) {
4464 Definition* arg = call->ArgumentAt(1);
4465 AddCheckSmi(arg, call->deopt_id(), call->env(), call);
4466 ReplaceCall(call,
4467 new(I) SmiToDoubleInstr(new(I) Value(arg),
4468 call->token_pos()));
4469 } else if (ArgIsAlways(kMintCid, ic_data, 1) &&
4470 CanConvertUnboxedMintToDouble()) {
4471 Definition* arg = call->ArgumentAt(1);
4472 ReplaceCall(call,
4473 new(I) MintToDoubleInstr(new(I) Value(arg),
4474 call->deopt_id()));
4475 }
4476 }
4477 }
4478 } else if (call->function().IsFactory()) {
4479 const Class& function_class =
4480 Class::Handle(I, call->function().Owner());
4481 if ((function_class.library() == Library::CoreLibrary()) ||
4482 (function_class.library() == Library::TypedDataLibrary())) {
4483 intptr_t cid = FactoryRecognizer::ResultCid(call->function());
4484 switch (cid) {
4485 case kArrayCid: {
4486 Value* type = new(I) Value(call->ArgumentAt(0));
4487 Value* num_elements = new(I) Value(call->ArgumentAt(1));
4488 if (num_elements->BindsToConstant() &&
4489 num_elements->BoundConstant().IsSmi()) {
4490 intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
4491 if (length >= 0 && length <= Array::kMaxElements) {
4492 CreateArrayInstr* create_array =
4493 new(I) CreateArrayInstr(
4494 call->token_pos(), type, num_elements);
4495 ReplaceCall(call, create_array);
4496 }
4497 }
4498 }
4499 default:
4500 break;
4501 }
4502 }
4503 }
4504 }
4505
4506
4507 void FlowGraphOptimizer::VisitStoreInstanceField(
4508 StoreInstanceFieldInstr* instr) {
4509 if (instr->IsUnboxedStore()) {
4510 ASSERT(instr->is_initialization_);
4511 // Determine if this field should be unboxed based on the usage of getter
4512 // and setter functions: The heuristic requires that the setter has a
4513 // usage count of at least 1/kGetterSetterRatio of the getter usage count.
4514 // This is to avoid unboxing fields where the setter is never or rarely
4515 // executed.
4516 const Field& field = Field::ZoneHandle(I, instr->field().raw());
4517 const String& field_name = String::Handle(I, field.name());
4518 const Class& owner = Class::Handle(I, field.owner());
4519 const Function& getter =
4520 Function::Handle(I, owner.LookupGetterFunction(field_name));
4521 const Function& setter =
4522 Function::Handle(I, owner.LookupSetterFunction(field_name));
4523 bool result = !getter.IsNull()
4524 && !setter.IsNull()
4525 && (setter.usage_counter() > 0)
4526 && (FLAG_getter_setter_ratio * setter.usage_counter() >=
4527 getter.usage_counter());
4528 if (!result) {
4529 if (FLAG_trace_optimization) {
4530 OS::Print("Disabling unboxing of %s\n", field.ToCString());
4531 }
4532 field.set_is_unboxing_candidate(false);
4533 field.DeoptimizeDependentCode();
4534 } else {
4535 FlowGraph::AddToGuardedFields(flow_graph_->guarded_fields(), &field);
4536 }
4537 }
4538 }
4539
4540
4541 void FlowGraphOptimizer::VisitAllocateContext(AllocateContextInstr* instr) {
4542 // Replace generic allocation with a sequence of inlined allocation and
4543 // explicit initalizing stores.
4544 AllocateUninitializedContextInstr* replacement =
4545 new AllocateUninitializedContextInstr(instr->token_pos(),
4546 instr->num_context_variables());
4547 instr->ReplaceWith(replacement, current_iterator());
4548
4549 StoreInstanceFieldInstr* store =
4550 new(I) StoreInstanceFieldInstr(Context::parent_offset(),
4551 new Value(replacement),
4552 new Value(flow_graph_->constant_null()),
4553 kNoStoreBarrier,
4554 instr->token_pos());
4555 store->set_is_initialization(true); // Won't be eliminated by DSE.
4556 flow_graph_->InsertAfter(replacement, store, NULL, FlowGraph::kEffect);
4557 Definition* cursor = store;
4558 for (intptr_t i = 0; i < instr->num_context_variables(); ++i) {
4559 store =
4560 new(I) StoreInstanceFieldInstr(Context::variable_offset(i),
4561 new Value(replacement),
4562 new Value(flow_graph_->constant_null()),
4563 kNoStoreBarrier,
4564 instr->token_pos());
4565 store->set_is_initialization(true); // Won't be eliminated by DSE.
4566 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect);
4567 cursor = store;
4568 }
4569 }
4570
4571
4572 void FlowGraphOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) {
4573 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized.
4574 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM)
4575 if (!instr->can_pack_into_smi())
4576 instr->set_representation(kUnboxedMint);
4577 #endif
4578 }
4579
4580
4581 bool FlowGraphOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr,
4582 const ICData& unary_ic_data) {
4583 ASSERT((unary_ic_data.NumberOfChecks() > 0) &&
4584 (unary_ic_data.NumArgsTested() == 1));
4585 if (FLAG_enable_type_checks) {
4586 // Checked mode setters are inlined like normal methods by conventional
4587 // inlining.
4588 return false;
4589 }
4590
4591 ASSERT(instr->HasICData());
4592 if (unary_ic_data.NumberOfChecks() == 0) {
4593 // No type feedback collected.
4594 return false;
4595 }
4596 if (!unary_ic_data.HasOneTarget()) {
4597 // Polymorphic sites are inlined like normal method calls by conventional
4598 // inlining.
4599 return false;
4600 }
4601 Function& target = Function::Handle(I);
4602 intptr_t class_id;
4603 unary_ic_data.GetOneClassCheckAt(0, &class_id, &target);
4604 if (target.kind() != RawFunction::kImplicitSetter) {
4605 // Non-implicit setter are inlined like normal method calls.
4606 return false;
4607 }
4608 // Inline implicit instance setter.
4609 const String& field_name =
4610 String::Handle(I, Field::NameFromSetter(instr->function_name()));
4611 const Field& field =
4612 Field::ZoneHandle(I, GetField(class_id, field_name));
4613 ASSERT(!field.IsNull());
4614
4615 if (InstanceCallNeedsClassCheck(instr, RawFunction::kImplicitSetter)) {
4616 AddReceiverCheck(instr);
4617 }
4618 StoreBarrierType needs_store_barrier = kEmitStoreBarrier;
4619 if (ArgIsAlways(kSmiCid, *instr->ic_data(), 1)) {
4620 InsertBefore(instr,
4621 new(I) CheckSmiInstr(
4622 new(I) Value(instr->ArgumentAt(1)),
4623 instr->deopt_id(),
4624 instr->token_pos()),
4625 instr->env(),
4626 FlowGraph::kEffect);
4627 needs_store_barrier = kNoStoreBarrier;
4628 }
4629
4630 if (field.guarded_cid() != kDynamicCid) {
4631 InsertBefore(instr,
4632 new(I) GuardFieldClassInstr(
4633 new(I) Value(instr->ArgumentAt(1)),
4634 field,
4635 instr->deopt_id()),
4636 instr->env(),
4637 FlowGraph::kEffect);
4638 }
4639
4640 if (field.needs_length_check()) {
4641 InsertBefore(instr,
4642 new(I) GuardFieldLengthInstr(
4643 new(I) Value(instr->ArgumentAt(1)),
4644 field,
4645 instr->deopt_id()),
4646 instr->env(),
4647 FlowGraph::kEffect);
4648 }
4649
4650 // Field guard was detached.
4651 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr(
4652 field,
4653 new(I) Value(instr->ArgumentAt(0)),
4654 new(I) Value(instr->ArgumentAt(1)),
4655 needs_store_barrier,
4656 instr->token_pos());
4657
4658 if (store->IsUnboxedStore()) {
4659 FlowGraph::AddToGuardedFields(flow_graph_->guarded_fields(), &field);
4660 }
4661
4662 // Discard the environment from the original instruction because the store
4663 // can't deoptimize.
4664 instr->RemoveEnvironment();
4665 ReplaceCall(instr, store);
4666 return true;
4667 }
4668
4669
4670 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32)
4671 // Smi widening pass is only meaningful on platforms where Smi
4672 // is smaller than 32bit. For now only support it on ARM and ia32.
4673 static bool CanBeWidened(BinarySmiOpInstr* smi_op) {
4674 return BinaryInt32OpInstr::IsSupported(smi_op->op_kind(),
4675 smi_op->left(),
4676 smi_op->right());
4677 }
4678
4679
4680 static bool BenefitsFromWidening(BinarySmiOpInstr* smi_op) {
4681 // TODO(vegorov): when shifts with non-constants shift count are supported
4682 // add them here as we save untagging for the count.
4683 switch (smi_op->op_kind()) {
4684 case Token::kMUL:
4685 case Token::kSHR:
4686 // For kMUL we save untagging of the argument for kSHR
4687 // we save tagging of the result.
4688 return true;
4689
4690 default:
4691 return false;
4692 }
4693 }
4694
4695
4696 void FlowGraphOptimizer::WidenSmiToInt32() {
4697 GrowableArray<BinarySmiOpInstr*> candidates;
4698
4699 // Step 1. Collect all instructions that potentially benefit from widening of
4700 // their operands (or their result) into int32 range.
4701 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
4702 !block_it.Done();
4703 block_it.Advance()) {
4704 for (ForwardInstructionIterator instr_it(block_it.Current());
4705 !instr_it.Done();
4706 instr_it.Advance()) {
4707 BinarySmiOpInstr* smi_op = instr_it.Current()->AsBinarySmiOp();
4708 if ((smi_op != NULL) &&
4709 BenefitsFromWidening(smi_op) &&
4710 CanBeWidened(smi_op)) {
4711 candidates.Add(smi_op);
4712 }
4713 }
4714 }
4715
4716 if (candidates.is_empty()) {
4717 return;
4718 }
4719
4720 // Step 2. For each block in the graph compute which loop it belongs to.
4721 // We will use this information later during computation of the widening's
4722 // gain: we are going to assume that only conversion occuring inside the
4723 // same loop should be counted against the gain, all other conversions
4724 // can be hoisted and thus cost nothing compared to the loop cost itself.
4725 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers =
4726 flow_graph()->LoopHeaders();
4727
4728 GrowableArray<intptr_t> loops(flow_graph_->preorder().length());
4729 for (intptr_t i = 0; i < flow_graph_->preorder().length(); i++) {
4730 loops.Add(-1);
4731 }
4732
4733 for (intptr_t loop_id = 0; loop_id < loop_headers.length(); ++loop_id) {
4734 for (BitVector::Iterator loop_it(loop_headers[loop_id]->loop_info());
4735 !loop_it.Done();
4736 loop_it.Advance()) {
4737 loops[loop_it.Current()] = loop_id;
4738 }
4739 }
4740
4741 // Step 3. For each candidate transitively collect all other BinarySmiOpInstr
4742 // and PhiInstr that depend on it and that it depends on and count amount of
4743 // untagging operations that we save in assumption that this whole graph of
4744 // values is using kUnboxedInt32 representation instead of kTagged.
4745 // Convert those graphs that have positive gain to kUnboxedInt32.
4746
4747 // BitVector containing SSA indexes of all processed definitions. Used to skip
4748 // those candidates that belong to dependency graph of another candidate.
4749 BitVector* processed =
4750 new(I) BitVector(I, flow_graph_->current_ssa_temp_index());
4751
4752 // Worklist used to collect dependency graph.
4753 DefinitionWorklist worklist(flow_graph_, candidates.length());
4754 for (intptr_t i = 0; i < candidates.length(); i++) {
4755 BinarySmiOpInstr* op = candidates[i];
4756 if (op->WasEliminated() || processed->Contains(op->ssa_temp_index())) {
4757 continue;
4758 }
4759
4760 if (FLAG_trace_smi_widening) {
4761 OS::Print("analysing candidate: %s\n", op->ToCString());
4762 }
4763 worklist.Clear();
4764 worklist.Add(op);
4765
4766 // Collect dependency graph. Note: more items are added to worklist
4767 // inside this loop.
4768 intptr_t gain = 0;
4769 for (intptr_t j = 0; j < worklist.definitions().length(); j++) {
4770 Definition* defn = worklist.definitions()[j];
4771
4772 if (FLAG_trace_smi_widening) {
4773 OS::Print("> %s\n", defn->ToCString());
4774 }
4775
4776 if (defn->IsBinarySmiOp() &&
4777 BenefitsFromWidening(defn->AsBinarySmiOp())) {
4778 gain++;
4779 if (FLAG_trace_smi_widening) {
4780 OS::Print("^ [%" Pd "] (o) %s\n", gain, defn->ToCString());
4781 }
4782 }
4783
4784 const intptr_t defn_loop = loops[defn->GetBlock()->preorder_number()];
4785
4786 // Process all inputs.
4787 for (intptr_t k = 0; k < defn->InputCount(); k++) {
4788 Definition* input = defn->InputAt(k)->definition();
4789 if (input->IsBinarySmiOp() &&
4790 CanBeWidened(input->AsBinarySmiOp())) {
4791 worklist.Add(input);
4792 } else if (input->IsPhi() && (input->Type()->ToCid() == kSmiCid)) {
4793 worklist.Add(input);
4794 } else if (input->IsBinaryMintOp()) {
4795 // Mint operation produces untagged result. We avoid tagging.
4796 gain++;
4797 if (FLAG_trace_smi_widening) {
4798 OS::Print("^ [%" Pd "] (i) %s\n", gain, input->ToCString());
4799 }
4800 } else if (defn_loop == loops[input->GetBlock()->preorder_number()] &&
4801 (input->Type()->ToCid() == kSmiCid)) {
4802 // Input comes from the same loop, is known to be smi and requires
4803 // untagging.
4804 // TODO(vegorov) this heuristic assumes that values that are not
4805 // known to be smi have to be checked and this check can be
4806 // coalesced with untagging. Start coalescing them.
4807 gain--;
4808 if (FLAG_trace_smi_widening) {
4809 OS::Print("v [%" Pd "] (i) %s\n", gain, input->ToCString());
4810 }
4811 }
4812 }
4813
4814 // Process all uses.
4815 for (Value* use = defn->input_use_list();
4816 use != NULL;
4817 use = use->next_use()) {
4818 Instruction* instr = use->instruction();
4819 Definition* use_defn = instr->AsDefinition();
4820 if (use_defn == NULL) {
4821 // We assume that tagging before returning or pushing argument costs
4822 // very little compared to the cost of the return/call itself.
4823 if (!instr->IsReturn() && !instr->IsPushArgument()) {
4824 gain--;
4825 if (FLAG_trace_smi_widening) {
4826 OS::Print("v [%" Pd "] (u) %s\n",
4827 gain,
4828 use->instruction()->ToCString());
4829 }
4830 }
4831 continue;
4832 } else if (use_defn->IsBinarySmiOp() &&
4833 CanBeWidened(use_defn->AsBinarySmiOp())) {
4834 worklist.Add(use_defn);
4835 } else if (use_defn->IsPhi() &&
4836 use_defn->AsPhi()->Type()->ToCid() == kSmiCid) {
4837 worklist.Add(use_defn);
4838 } else if (use_defn->IsBinaryMintOp()) {
4839 // BinaryMintOp requires untagging of its inputs.
4840 // Converting kUnboxedInt32 to kUnboxedMint is essentially zero cost
4841 // sign extension operation.
4842 gain++;
4843 if (FLAG_trace_smi_widening) {
4844 OS::Print("^ [%" Pd "] (u) %s\n",
4845 gain,
4846 use->instruction()->ToCString());
4847 }
4848 } else if (defn_loop == loops[instr->GetBlock()->preorder_number()]) {
4849 gain--;
4850 if (FLAG_trace_smi_widening) {
4851 OS::Print("v [%" Pd "] (u) %s\n",
4852 gain,
4853 use->instruction()->ToCString());
4854 }
4855 }
4856 }
4857 }
4858
4859 processed->AddAll(worklist.contains_vector());
4860
4861 if (FLAG_trace_smi_widening) {
4862 OS::Print("~ %s gain %" Pd "\n", op->ToCString(), gain);
4863 }
4864
4865 if (gain > 0) {
4866 // We have positive gain from widening. Convert all BinarySmiOpInstr into
4867 // BinaryInt32OpInstr and set representation of all phis to kUnboxedInt32.
4868 for (intptr_t j = 0; j < worklist.definitions().length(); j++) {
4869 Definition* defn = worklist.definitions()[j];
4870 ASSERT(defn->IsPhi() || defn->IsBinarySmiOp());
4871
4872 if (defn->IsBinarySmiOp()) {
4873 BinarySmiOpInstr* smi_op = defn->AsBinarySmiOp();
4874 BinaryInt32OpInstr* int32_op = new(I) BinaryInt32OpInstr(
4875 smi_op->op_kind(),
4876 smi_op->left()->CopyWithType(),
4877 smi_op->right()->CopyWithType(),
4878 smi_op->DeoptimizationTarget());
4879
4880 smi_op->ReplaceWith(int32_op, NULL);
4881 } else if (defn->IsPhi()) {
4882 defn->AsPhi()->set_representation(kUnboxedInt32);
4883 ASSERT(defn->Type()->IsInt());
4884 }
4885 }
4886 }
4887 }
4888 }
4889 #else
4890 void FlowGraphOptimizer::WidenSmiToInt32() {
4891 // TODO(vegorov) ideally on 64-bit platforms we would like to narrow smi
4892 // operations to 32-bit where it saves tagging and untagging and allows
4893 // to use shorted (and faster) instructions. But we currently don't
4894 // save enough range information in the ICData to drive this decision.
4895 }
4896 #endif
4897
4898 void FlowGraphOptimizer::InferIntRanges() {
4899 RangeAnalysis range_analysis(flow_graph_);
4900 range_analysis.Analyze();
4901 }
4902
4903
4904 void TryCatchAnalyzer::Optimize(FlowGraph* flow_graph) {
4905 // For every catch-block: Iterate over all call instructions inside the
4906 // corresponding try-block and figure out for each environment value if it
4907 // is the same constant at all calls. If yes, replace the initial definition
4908 // at the catch-entry with this constant.
4909 const GrowableArray<CatchBlockEntryInstr*>& catch_entries =
4910 flow_graph->graph_entry()->catch_entries();
4911 intptr_t base = kFirstLocalSlotFromFp + flow_graph->num_non_copied_params();
4912 for (intptr_t catch_idx = 0;
4913 catch_idx < catch_entries.length();
4914 ++catch_idx) {
4915 CatchBlockEntryInstr* catch_entry = catch_entries[catch_idx];
4916
4917 // Initialize cdefs with the original initial definitions (ParameterInstr).
4918 // The following representation is used:
4919 // ParameterInstr => unknown
4920 // ConstantInstr => known constant
4921 // NULL => non-constant
4922 GrowableArray<Definition*>* idefs = catch_entry->initial_definitions();
4923 GrowableArray<Definition*> cdefs(idefs->length());
4924 cdefs.AddArray(*idefs);
4925
4926 // exception_var and stacktrace_var are never constant.
4927 intptr_t ex_idx = base - catch_entry->exception_var().index();
4928 intptr_t st_idx = base - catch_entry->stacktrace_var().index();
4929 cdefs[ex_idx] = cdefs[st_idx] = NULL;
4930
4931 for (BlockIterator block_it = flow_graph->reverse_postorder_iterator();
4932 !block_it.Done();
4933 block_it.Advance()) {
4934 BlockEntryInstr* block = block_it.Current();
4935 if (block->try_index() == catch_entry->catch_try_index()) {
4936 for (ForwardInstructionIterator instr_it(block);
4937 !instr_it.Done();
4938 instr_it.Advance()) {
4939 Instruction* current = instr_it.Current();
4940 if (current->MayThrow()) {
4941 Environment* env = current->env()->Outermost();
4942 ASSERT(env != NULL);
4943 for (intptr_t env_idx = 0; env_idx < cdefs.length(); ++env_idx) {
4944 if (cdefs[env_idx] != NULL &&
4945 env->ValueAt(env_idx)->BindsToConstant()) {
4946 cdefs[env_idx] = env->ValueAt(env_idx)->definition();
4947 }
4948 if (cdefs[env_idx] != env->ValueAt(env_idx)->definition()) {
4949 cdefs[env_idx] = NULL;
4950 }
4951 }
4952 }
4953 }
4954 }
4955 }
4956 for (intptr_t j = 0; j < idefs->length(); ++j) {
4957 if (cdefs[j] != NULL && cdefs[j]->IsConstant()) {
4958 // TODO(fschneider): Use constants from the constant pool.
4959 Definition* old = (*idefs)[j];
4960 ConstantInstr* orig = cdefs[j]->AsConstant();
4961 ConstantInstr* copy =
4962 new(flow_graph->isolate()) ConstantInstr(orig->value());
4963 copy->set_ssa_temp_index(flow_graph->alloc_ssa_temp_index());
4964 old->ReplaceUsesWith(copy);
4965 (*idefs)[j] = copy;
4966 }
4967 }
4968 }
4969 }
4970
4971
4972 LICM::LICM(FlowGraph* flow_graph) : flow_graph_(flow_graph) {
4973 ASSERT(flow_graph->is_licm_allowed());
4974 }
4975
4976
4977 void LICM::Hoist(ForwardInstructionIterator* it,
4978 BlockEntryInstr* pre_header,
4979 Instruction* current) {
4980 if (current->IsCheckClass()) {
4981 current->AsCheckClass()->set_licm_hoisted(true);
4982 } else if (current->IsCheckSmi()) {
4983 current->AsCheckSmi()->set_licm_hoisted(true);
4984 } else if (current->IsCheckEitherNonSmi()) {
4985 current->AsCheckEitherNonSmi()->set_licm_hoisted(true);
4986 } else if (current->IsCheckArrayBound()) {
4987 current->AsCheckArrayBound()->set_licm_hoisted(true);
4988 }
4989 if (FLAG_trace_optimization) {
4990 OS::Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
4991 current->DebugName(),
4992 current->GetDeoptId(),
4993 current->GetBlock()->block_id(),
4994 pre_header->block_id());
4995 }
4996 // Move the instruction out of the loop.
4997 current->RemoveEnvironment();
4998 if (it != NULL) {
4999 it->RemoveCurrentFromGraph();
5000 } else {
5001 current->RemoveFromGraph();
5002 }
5003 GotoInstr* last = pre_header->last_instruction()->AsGoto();
5004 // Using kind kEffect will not assign a fresh ssa temporary index.
5005 flow_graph()->InsertBefore(last, current, last->env(), FlowGraph::kEffect);
5006 current->CopyDeoptIdFrom(*last);
5007 }
5008
5009
5010 void LICM::TrySpecializeSmiPhi(PhiInstr* phi,
5011 BlockEntryInstr* header,
5012 BlockEntryInstr* pre_header) {
5013 if (phi->Type()->ToCid() == kSmiCid) {
5014 return;
5015 }
5016
5017 // Check if there is only a single kDynamicCid input to the phi that
5018 // comes from the pre-header.
5019 const intptr_t kNotFound = -1;
5020 intptr_t non_smi_input = kNotFound;
5021 for (intptr_t i = 0; i < phi->InputCount(); ++i) {
5022 Value* input = phi->InputAt(i);
5023 if (input->Type()->ToCid() != kSmiCid) {
5024 if ((non_smi_input != kNotFound) ||
5025 (input->Type()->ToCid() != kDynamicCid)) {
5026 // There are multiple kDynamicCid inputs or there is an input that is
5027 // known to be non-smi.
5028 return;
5029 } else {
5030 non_smi_input = i;
5031 }
5032 }
5033 }
5034
5035 if ((non_smi_input == kNotFound) ||
5036 (phi->block()->PredecessorAt(non_smi_input) != pre_header)) {
5037 return;
5038 }
5039
5040 CheckSmiInstr* check = NULL;
5041 for (Value* use = phi->input_use_list();
5042 (use != NULL) && (check == NULL);
5043 use = use->next_use()) {
5044 check = use->instruction()->AsCheckSmi();
5045 }
5046
5047 if (check == NULL) {
5048 return;
5049 }
5050
5051 // Host CheckSmi instruction and make this phi smi one.
5052 Hoist(NULL, pre_header, check);
5053
5054 // Replace value we are checking with phi's input.
5055 check->value()->BindTo(phi->InputAt(non_smi_input)->definition());
5056
5057 phi->UpdateType(CompileType::FromCid(kSmiCid));
5058 }
5059
5060
5061 // Load instructions handled by load elimination.
5062 static bool IsLoadEliminationCandidate(Instruction* instr) {
5063 return instr->IsLoadField()
5064 || instr->IsLoadIndexed()
5065 || instr->IsLoadStaticField();
5066 }
5067
5068
5069 static bool IsLoopInvariantLoad(ZoneGrowableArray<BitVector*>* sets,
5070 intptr_t loop_header_index,
5071 Instruction* instr) {
5072 return IsLoadEliminationCandidate(instr) &&
5073 (sets != NULL) &&
5074 instr->HasPlaceId() &&
5075 ((*sets)[loop_header_index] != NULL) &&
5076 (*sets)[loop_header_index]->Contains(instr->place_id());
5077 }
5078
5079
5080 void LICM::OptimisticallySpecializeSmiPhis() {
5081 if (!flow_graph()->parsed_function()->function().
5082 allows_hoisting_check_class()) {
5083 // Do not hoist any.
5084 return;
5085 }
5086
5087 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers =
5088 flow_graph()->LoopHeaders();
5089
5090 for (intptr_t i = 0; i < loop_headers.length(); ++i) {
5091 JoinEntryInstr* header = loop_headers[i]->AsJoinEntry();
5092 // Skip loop that don't have a pre-header block.
5093 BlockEntryInstr* pre_header = header->ImmediateDominator();
5094 if (pre_header == NULL) continue;
5095
5096 for (PhiIterator it(header); !it.Done(); it.Advance()) {
5097 TrySpecializeSmiPhi(it.Current(), header, pre_header);
5098 }
5099 }
5100 }
5101
5102
5103 void LICM::Optimize() {
5104 if (!flow_graph()->parsed_function()->function().
5105 allows_hoisting_check_class()) {
5106 // Do not hoist any.
5107 return;
5108 }
5109
5110 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers =
5111 flow_graph()->LoopHeaders();
5112
5113 ZoneGrowableArray<BitVector*>* loop_invariant_loads =
5114 flow_graph()->loop_invariant_loads();
5115
5116 BlockEffects* block_effects = flow_graph()->block_effects();
5117
5118 for (intptr_t i = 0; i < loop_headers.length(); ++i) {
5119 BlockEntryInstr* header = loop_headers[i];
5120 // Skip loop that don't have a pre-header block.
5121 BlockEntryInstr* pre_header = header->ImmediateDominator();
5122 if (pre_header == NULL) continue;
5123
5124 for (BitVector::Iterator loop_it(header->loop_info());
5125 !loop_it.Done();
5126 loop_it.Advance()) {
5127 BlockEntryInstr* block = flow_graph()->preorder()[loop_it.Current()];
5128 for (ForwardInstructionIterator it(block);
5129 !it.Done();
5130 it.Advance()) {
5131 Instruction* current = it.Current();
5132 if ((current->AllowsCSE() &&
5133 block_effects->CanBeMovedTo(current, pre_header)) ||
5134 IsLoopInvariantLoad(loop_invariant_loads, i, current)) {
5135 bool inputs_loop_invariant = true;
5136 for (int i = 0; i < current->InputCount(); ++i) {
5137 Definition* input_def = current->InputAt(i)->definition();
5138 if (!input_def->GetBlock()->Dominates(pre_header)) {
5139 inputs_loop_invariant = false;
5140 break;
5141 }
5142 }
5143 if (inputs_loop_invariant &&
5144 !current->IsAssertAssignable() &&
5145 !current->IsAssertBoolean()) {
5146 // TODO(fschneider): Enable hoisting of Assert-instructions
5147 // if it safe to do.
5148 Hoist(&it, pre_header, current);
5149 }
5150 }
5151 }
5152 }
5153 }
5154 }
5155
5156
5157 // Place describes an abstract location (e.g. field) that IR can load
5158 // from or store to.
5159 //
5160 // Places are also used to describe wild-card locations also known as aliases,
5161 // that essentially represent sets of places that alias each other. Places A
5162 // and B are said to alias each other if store into A can affect load from B.
5163 //
5164 // We distinguish the following aliases:
5165 //
5166 // - for fields
5167 // - *.f, *.@offs - field inside some object;
5168 // - X.f, X.@offs - field inside an allocated object X;
5169 // - for indexed accesses
5170 // - *[*] - non-constant index inside some object;
5171 // - *[C] - constant index inside some object;
5172 // - X[*] - non-constant index inside an allocated object X;
5173 // - X[C] - constant index inside an allocated object X.
5174 //
5175 // Separating allocations from other objects improves precision of the
5176 // load forwarding pass because of the following two properties:
5177 //
5178 // - if X can be proven to have no aliases itself (i.e. there is no other SSA
5179 // variable that points to X) then no place inside X can be aliased with any
5180 // wildcard dependent place (*.f, *.@offs, *[*], *[C]);
5181 // - given allocations X and Y no place inside X can be aliased with any place
5182 // inside Y even if any of them or both escape.
5183 //
5184 // It important to realize that single place can belong to multiple aliases.
5185 // For example place X.f with aliased allocation X belongs both to X.f and *.f
5186 // aliases. Likewise X[C] with non-aliased allocation X belongs to X[C] and X[*]
5187 // aliases.
5188 //
5189 class Place : public ValueObject {
5190 public:
5191 enum Kind {
5192 kNone,
5193
5194 // Field location. For instance fields is represented as a pair of a Field
5195 // object and an instance (SSA definition) that is being accessed.
5196 // For static fields instance is NULL.
5197 kField,
5198
5199 // VMField location. Represented as a pair of an instance (SSA definition)
5200 // being accessed and offset to the field.
5201 kVMField,
5202
5203 // Indexed location with a non-constant index.
5204 kIndexed,
5205
5206 // Indexed location with a constant index.
5207 kConstantIndexed,
5208 };
5209
5210 Place(const Place& other)
5211 : ValueObject(),
5212 kind_(other.kind_),
5213 representation_(other.representation_),
5214 instance_(other.instance_),
5215 raw_selector_(other.raw_selector_),
5216 id_(other.id_) {
5217 }
5218
5219 // Construct a place from instruction if instruction accesses any place.
5220 // Otherwise constructs kNone place.
5221 Place(Instruction* instr, bool* is_load, bool* is_store)
5222 : kind_(kNone),
5223 representation_(kNoRepresentation),
5224 instance_(NULL),
5225 raw_selector_(0),
5226 id_(0) {
5227 switch (instr->tag()) {
5228 case Instruction::kLoadField: {
5229 LoadFieldInstr* load_field = instr->AsLoadField();
5230 representation_ = load_field->representation();
5231 instance_ = load_field->instance()->definition()->OriginalDefinition();
5232 if (load_field->field() != NULL) {
5233 kind_ = kField;
5234 field_ = load_field->field();
5235 } else {
5236 kind_ = kVMField;
5237 offset_in_bytes_ = load_field->offset_in_bytes();
5238 }
5239 *is_load = true;
5240 break;
5241 }
5242
5243 case Instruction::kStoreInstanceField: {
5244 StoreInstanceFieldInstr* store =
5245 instr->AsStoreInstanceField();
5246 representation_ = store->RequiredInputRepresentation(
5247 StoreInstanceFieldInstr::kValuePos);
5248 instance_ = store->instance()->definition()->OriginalDefinition();
5249 if (!store->field().IsNull()) {
5250 kind_ = kField;
5251 field_ = &store->field();
5252 } else {
5253 kind_ = kVMField;
5254 offset_in_bytes_ = store->offset_in_bytes();
5255 }
5256 *is_store = true;
5257 break;
5258 }
5259
5260 case Instruction::kLoadStaticField:
5261 kind_ = kField;
5262 representation_ = instr->AsLoadStaticField()->representation();
5263 field_ = &instr->AsLoadStaticField()->StaticField();
5264 *is_load = true;
5265 break;
5266
5267 case Instruction::kStoreStaticField:
5268 kind_ = kField;
5269 representation_ = instr->AsStoreStaticField()->
5270 RequiredInputRepresentation(StoreStaticFieldInstr::kValuePos);
5271 field_ = &instr->AsStoreStaticField()->field();
5272 *is_store = true;
5273 break;
5274
5275 case Instruction::kLoadIndexed: {
5276 LoadIndexedInstr* load_indexed = instr->AsLoadIndexed();
5277 representation_ = load_indexed->representation();
5278 instance_ = load_indexed->array()->definition()->OriginalDefinition();
5279 SetIndex(load_indexed->index()->definition());
5280 *is_load = true;
5281 break;
5282 }
5283
5284 case Instruction::kStoreIndexed: {
5285 StoreIndexedInstr* store_indexed = instr->AsStoreIndexed();
5286 representation_ = store_indexed->
5287 RequiredInputRepresentation(StoreIndexedInstr::kValuePos);
5288 instance_ = store_indexed->array()->definition()->OriginalDefinition();
5289 SetIndex(store_indexed->index()->definition());
5290 *is_store = true;
5291 break;
5292 }
5293
5294 default:
5295 break;
5296 }
5297 }
5298
5299 // Create object representing *[*] alias.
5300 static Place* CreateAnyInstanceAnyIndexAlias(Isolate* isolate,
5301 intptr_t id) {
5302 return Wrap(isolate, Place(kIndexed, NULL, 0), id);
5303 }
5304
5305 // Return least generic alias for this place. Given that aliases are
5306 // essentially sets of places we define least generic alias as a smallest
5307 // alias that contains this place.
5308 //
5309 // We obtain such alias by a simple transformation:
5310 //
5311 // - for places that depend on an instance X.f, X.@offs, X[i], X[C]
5312 // we drop X if X is not an allocation because in this case X does not
5313 // posess an identity obtaining aliases *.f, *.@offs, *[i] and *[C]
5314 // respectively;
5315 // - for non-constant indexed places X[i] we drop information about the
5316 // index obtaining alias X[*].
5317 //
5318 Place ToAlias() const {
5319 return Place(
5320 kind_,
5321 (DependsOnInstance() && IsAllocation(instance())) ? instance() : NULL,
5322 (kind() == kIndexed) ? 0 : raw_selector_);
5323 }
5324
5325 bool DependsOnInstance() const {
5326 switch (kind()) {
5327 case kField:
5328 case kVMField:
5329 case kIndexed:
5330 case kConstantIndexed:
5331 return true;
5332
5333 case kNone:
5334 return false;
5335 }
5336
5337 UNREACHABLE();
5338 return false;
5339 }
5340
5341 // Given instance dependent alias X.f, X.@offs, X[C], X[*] return
5342 // wild-card dependent alias *.f, *.@offs, *[C] or *[*] respectively.
5343 Place CopyWithoutInstance() const {
5344 ASSERT(DependsOnInstance());
5345 return Place(kind_, NULL, raw_selector_);
5346 }
5347
5348 // Given alias X[C] or *[C] return X[*] and *[*] respectively.
5349 Place CopyWithoutIndex() const {
5350 ASSERT(kind_ == kConstantIndexed);
5351 return Place(kIndexed, instance_, 0);
5352 }
5353
5354 intptr_t id() const { return id_; }
5355
5356 Kind kind() const { return kind_; }
5357
5358 Representation representation() const { return representation_; }
5359
5360 Definition* instance() const {
5361 ASSERT(DependsOnInstance());
5362 return instance_;
5363 }
5364
5365 void set_instance(Definition* def) {
5366 ASSERT(DependsOnInstance());
5367 instance_ = def->OriginalDefinition();
5368 }
5369
5370 const Field& field() const {
5371 ASSERT(kind_ == kField);
5372 return *field_;
5373 }
5374
5375 intptr_t offset_in_bytes() const {
5376 ASSERT(kind_ == kVMField);
5377 return offset_in_bytes_;
5378 }
5379
5380 Definition* index() const {
5381 ASSERT(kind_ == kIndexed);
5382 return index_;
5383 }
5384
5385 intptr_t index_constant() const {
5386 ASSERT(kind_ == kConstantIndexed);
5387 return index_constant_;
5388 }
5389
5390 static const char* DefinitionName(Definition* def) {
5391 if (def == NULL) {
5392 return "*";
5393 } else {
5394 return Isolate::Current()->current_zone()->PrintToString(
5395 "v%" Pd, def->ssa_temp_index());
5396 }
5397 }
5398
5399 const char* ToCString() const {
5400 switch (kind_) {
5401 case kNone:
5402 return "<none>";
5403
5404 case kField: {
5405 const char* field_name = String::Handle(field().name()).ToCString();
5406 if (field().is_static()) {
5407 return Isolate::Current()->current_zone()->PrintToString(
5408 "<%s>", field_name);
5409 } else {
5410 return Isolate::Current()->current_zone()->PrintToString(
5411 "<%s.%s>", DefinitionName(instance()), field_name);
5412 }
5413 }
5414
5415 case kVMField:
5416 return Isolate::Current()->current_zone()->PrintToString(
5417 "<%s.@%" Pd ">",
5418 DefinitionName(instance()),
5419 offset_in_bytes());
5420
5421 case kIndexed:
5422 return Isolate::Current()->current_zone()->PrintToString(
5423 "<%s[%s]>",
5424 DefinitionName(instance()),
5425 DefinitionName(index()));
5426
5427 case kConstantIndexed:
5428 return Isolate::Current()->current_zone()->PrintToString(
5429 "<%s[%" Pd "]>",
5430 DefinitionName(instance()),
5431 index_constant());
5432 }
5433 UNREACHABLE();
5434 return "<?>";
5435 }
5436
5437 bool IsFinalField() const {
5438 return (kind() == kField) && field().is_final();
5439 }
5440
5441 intptr_t Hashcode() const {
5442 return (kind_ * 63 + reinterpret_cast<intptr_t>(instance_)) * 31 +
5443 representation_ * 15 + FieldHashcode();
5444 }
5445
5446 bool Equals(const Place* other) const {
5447 return (kind_ == other->kind_) &&
5448 (representation_ == other->representation_) &&
5449 (instance_ == other->instance_) &&
5450 SameField(other);
5451 }
5452
5453 // Create a zone allocated copy of this place and assign given id to it.
5454 static Place* Wrap(Isolate* isolate, const Place& place, intptr_t id);
5455
5456 static bool IsAllocation(Definition* defn) {
5457 return (defn != NULL) &&
5458 (defn->IsAllocateObject() ||
5459 defn->IsCreateArray() ||
5460 defn->IsAllocateUninitializedContext() ||
5461 (defn->IsStaticCall() &&
5462 defn->AsStaticCall()->IsRecognizedFactory()));
5463 }
5464
5465 private:
5466 Place(Kind kind, Definition* instance, intptr_t selector)
5467 : kind_(kind),
5468 representation_(kNoRepresentation),
5469 instance_(instance),
5470 raw_selector_(selector),
5471 id_(0) {
5472 }
5473
5474 bool SameField(const Place* other) const {
5475 return (kind_ == kField) ? (field().raw() == other->field().raw())
5476 : (offset_in_bytes_ == other->offset_in_bytes_);
5477 }
5478
5479 intptr_t FieldHashcode() const {
5480 return (kind_ == kField) ? reinterpret_cast<intptr_t>(field().raw())
5481 : offset_in_bytes_;
5482 }
5483
5484 void SetIndex(Definition* index) {
5485 ConstantInstr* index_constant = index->AsConstant();
5486 if ((index_constant != NULL) && index_constant->value().IsSmi()) {
5487 kind_ = kConstantIndexed;
5488 index_constant_ = Smi::Cast(index_constant->value()).Value();
5489 } else {
5490 kind_ = kIndexed;
5491 index_ = index;
5492 }
5493 }
5494
5495 Kind kind_;
5496 Representation representation_;
5497 Definition* instance_;
5498 union {
5499 intptr_t raw_selector_;
5500 const Field* field_;
5501 intptr_t offset_in_bytes_;
5502 intptr_t index_constant_;
5503 Definition* index_;
5504 };
5505
5506 intptr_t id_;
5507 };
5508
5509
5510 class ZonePlace : public ZoneAllocated {
5511 public:
5512 explicit ZonePlace(const Place& place) : place_(place) { }
5513
5514 Place* place() { return &place_; }
5515
5516 private:
5517 Place place_;
5518 };
5519
5520
5521 Place* Place::Wrap(Isolate* isolate, const Place& place, intptr_t id) {
5522 Place* wrapped = (new(isolate) ZonePlace(place))->place();
5523 wrapped->id_ = id;
5524 return wrapped;
5525 }
5526
5527
5528 // Correspondence between places connected through outgoing phi moves on the
5529 // edge that targets join.
5530 class PhiPlaceMoves : public ZoneAllocated {
5531 public:
5532 // Record a move from the place with id |from| to the place with id |to| at
5533 // the given block.
5534 void CreateOutgoingMove(Isolate* isolate,
5535 BlockEntryInstr* block, intptr_t from, intptr_t to) {
5536 const intptr_t block_num = block->preorder_number();
5537 while (moves_.length() <= block_num) {
5538 moves_.Add(NULL);
5539 }
5540
5541 if (moves_[block_num] == NULL) {
5542 moves_[block_num] = new(isolate) ZoneGrowableArray<Move>(5);
5543 }
5544
5545 moves_[block_num]->Add(Move(from, to));
5546 }
5547
5548 class Move {
5549 public:
5550 Move(intptr_t from, intptr_t to) : from_(from), to_(to) { }
5551
5552 intptr_t from() const { return from_; }
5553 intptr_t to() const { return to_; }
5554
5555 private:
5556 intptr_t from_;
5557 intptr_t to_;
5558 };
5559
5560 typedef const ZoneGrowableArray<Move>* MovesList;
5561
5562 MovesList GetOutgoingMoves(BlockEntryInstr* block) const {
5563 const intptr_t block_num = block->preorder_number();
5564 return (block_num < moves_.length()) ?
5565 moves_[block_num] : NULL;
5566 }
5567
5568 private:
5569 GrowableArray<ZoneGrowableArray<Move>* > moves_;
5570 };
5571
5572
5573 // A map from aliases to a set of places sharing the alias. Additionally
5574 // carries a set of places that can be aliased by side-effects, essentially
5575 // those that are affected by calls.
5576 class AliasedSet : public ZoneAllocated {
5577 public:
5578 AliasedSet(Isolate* isolate,
5579 DirectChainedHashMap<PointerKeyValueTrait<Place> >* places_map,
5580 ZoneGrowableArray<Place*>* places,
5581 PhiPlaceMoves* phi_moves)
5582 : isolate_(isolate),
5583 places_map_(places_map),
5584 places_(*places),
5585 phi_moves_(phi_moves),
5586 aliases_(5),
5587 aliases_map_(),
5588 representatives_(),
5589 killed_(),
5590 aliased_by_effects_(new(isolate) BitVector(isolate, places->length())) {
5591 InsertAlias(Place::CreateAnyInstanceAnyIndexAlias(isolate_,
5592 kAnyInstanceAnyIndexAlias));
5593 for (intptr_t i = 0; i < places_.length(); i++) {
5594 AddRepresentative(places_[i]);
5595 }
5596 ComputeKillSets();
5597 }
5598
5599 intptr_t LookupAliasId(const Place& alias) {
5600 const Place* result = aliases_map_.Lookup(&alias);
5601 return (result != NULL) ? result->id() : static_cast<intptr_t>(kNoAlias);
5602 }
5603
5604 BitVector* GetKilledSet(intptr_t alias) {
5605 return (alias < killed_.length()) ? killed_[alias] : NULL;
5606 }
5607
5608 intptr_t max_place_id() const { return places().length(); }
5609 bool IsEmpty() const { return max_place_id() == 0; }
5610
5611 BitVector* aliased_by_effects() const { return aliased_by_effects_; }
5612
5613 const ZoneGrowableArray<Place*>& places() const {
5614 return places_;
5615 }
5616
5617 Place* LookupCanonical(Place* place) const {
5618 return places_map_->Lookup(place);
5619 }
5620
5621 void PrintSet(BitVector* set) {
5622 bool comma = false;
5623 for (BitVector::Iterator it(set);
5624 !it.Done();
5625 it.Advance()) {
5626 if (comma) {
5627 OS::Print(", ");
5628 }
5629 OS::Print("%s", places_[it.Current()]->ToCString());
5630 comma = true;
5631 }
5632 }
5633
5634 const PhiPlaceMoves* phi_moves() const { return phi_moves_; }
5635
5636 void RollbackAliasedIdentites() {
5637 for (intptr_t i = 0; i < identity_rollback_.length(); ++i) {
5638 identity_rollback_[i]->SetIdentity(AliasIdentity::Unknown());
5639 }
5640 }
5641
5642 // Returns false if the result of an allocation instruction can't be aliased
5643 // by another SSA variable and true otherwise.
5644 bool CanBeAliased(Definition* alloc) {
5645 if (!Place::IsAllocation(alloc)) {
5646 return true;
5647 }
5648
5649 if (alloc->Identity().IsUnknown()) {
5650 ComputeAliasing(alloc);
5651 }
5652
5653 return !alloc->Identity().IsNotAliased();
5654 }
5655
5656 enum {
5657 kNoAlias = 0
5658 };
5659
5660 private:
5661 enum {
5662 // Artificial alias that is used to collect all representatives of the
5663 // *[C], X[C] aliases for arbitrary C.
5664 kAnyConstantIndexedAlias = 1,
5665
5666 // Artificial alias that is used to collect all representatives of
5667 // *[C] alias for arbitrary C.
5668 kUnknownInstanceConstantIndexedAlias = 2,
5669
5670 // Artificial alias that is used to collect all representatives of
5671 // X[*] alias for all X.
5672 kAnyAllocationIndexedAlias = 3,
5673
5674 // *[*] alias.
5675 kAnyInstanceAnyIndexAlias = 4
5676 };
5677
5678 // Compute least generic alias for the place and assign alias id to it.
5679 void AddRepresentative(Place* place) {
5680 if (!place->IsFinalField()) {
5681 const Place* alias = CanonicalizeAlias(place->ToAlias());
5682 EnsureSet(&representatives_, alias->id())->Add(place->id());
5683
5684 // Update cumulative representative sets that are used during
5685 // killed sets computation.
5686 if (alias->kind() == Place::kConstantIndexed) {
5687 if (CanBeAliased(alias->instance())) {
5688 EnsureSet(&representatives_, kAnyConstantIndexedAlias)->
5689 Add(place->id());
5690 }
5691
5692 if (alias->instance() == NULL) {
5693 EnsureSet(&representatives_, kUnknownInstanceConstantIndexedAlias)->
5694 Add(place->id());
5695 }
5696 } else if ((alias->kind() == Place::kIndexed) &&
5697 CanBeAliased(place->instance())) {
5698 EnsureSet(&representatives_, kAnyAllocationIndexedAlias)->
5699 Add(place->id());
5700 }
5701
5702 if (!IsIndependentFromEffects(place)) {
5703 aliased_by_effects_->Add(place->id());
5704 }
5705 }
5706 }
5707
5708 void ComputeKillSets() {
5709 for (intptr_t i = 0; i < aliases_.length(); ++i) {
5710 const Place* alias = aliases_[i];
5711 // Add all representatives to the kill set.
5712 AddAllRepresentatives(alias->id(), alias->id());
5713 ComputeKillSet(alias);
5714 }
5715
5716 if (FLAG_trace_load_optimization) {
5717 OS::Print("Aliases KILL sets:\n");
5718 for (intptr_t i = 0; i < aliases_.length(); ++i) {
5719 const Place* alias = aliases_[i];
5720 BitVector* kill = GetKilledSet(alias->id());
5721
5722 OS::Print("%s: ", alias->ToCString());
5723 if (kill != NULL) {
5724 PrintSet(kill);
5725 }
5726 OS::Print("\n");
5727 }
5728 }
5729 }
5730
5731 void InsertAlias(const Place* alias) {
5732 aliases_map_.Insert(alias);
5733 aliases_.Add(alias);
5734 }
5735
5736 const Place* CanonicalizeAlias(const Place& alias) {
5737 const Place* canonical = aliases_map_.Lookup(&alias);
5738 if (canonical == NULL) {
5739 canonical = Place::Wrap(isolate_,
5740 alias,
5741 kAnyInstanceAnyIndexAlias + aliases_.length());
5742 InsertAlias(canonical);
5743 }
5744 return canonical;
5745 }
5746
5747 BitVector* GetRepresentativesSet(intptr_t alias) {
5748 return (alias < representatives_.length()) ? representatives_[alias] : NULL;
5749 }
5750
5751 BitVector* EnsureSet(GrowableArray<BitVector*>* sets,
5752 intptr_t alias) {
5753 while (sets->length() <= alias) {
5754 sets->Add(NULL);
5755 }
5756
5757 BitVector* set = (*sets)[alias];
5758 if (set == NULL) {
5759 (*sets)[alias] = set = new(isolate_) BitVector(isolate_, max_place_id());
5760 }
5761 return set;
5762 }
5763
5764 void AddAllRepresentatives(const Place* to, intptr_t from) {
5765 AddAllRepresentatives(to->id(), from);
5766 }
5767
5768 void AddAllRepresentatives(intptr_t to, intptr_t from) {
5769 BitVector* from_set = GetRepresentativesSet(from);
5770 if (from_set != NULL) {
5771 EnsureSet(&killed_, to)->AddAll(from_set);
5772 }
5773 }
5774
5775 void CrossAlias(const Place* to, const Place& from) {
5776 const intptr_t from_id = LookupAliasId(from);
5777 if (from_id == kNoAlias) {
5778 return;
5779 }
5780 CrossAlias(to, from_id);
5781 }
5782
5783 void CrossAlias(const Place* to, intptr_t from) {
5784 AddAllRepresentatives(to->id(), from);
5785 AddAllRepresentatives(from, to->id());
5786 }
5787
5788 // When computing kill sets we let less generic alias insert its
5789 // representatives into more generic alias'es kill set. For example
5790 // when visiting alias X[*] instead of searching for all aliases X[C]
5791 // and inserting their representatives into kill set for X[*] we update
5792 // kill set for X[*] each time we visit new X[C] for some C.
5793 // There is an exception however: if both aliases are parametric like *[C]
5794 // and X[*] which cross alias when X is an aliased allocation then we use
5795 // artificial aliases that contain all possible representatives for the given
5796 // alias for any value of the parameter to compute resulting kill set.
5797 void ComputeKillSet(const Place* alias) {
5798 switch (alias->kind()) {
5799 case Place::kIndexed: // Either *[*] or X[*] alias.
5800 if (alias->instance() == NULL) {
5801 // *[*] aliases with X[*], X[C], *[C].
5802 AddAllRepresentatives(alias, kAnyConstantIndexedAlias);
5803 AddAllRepresentatives(alias, kAnyAllocationIndexedAlias);
5804 } else if (CanBeAliased(alias->instance())) {
5805 // X[*] aliases with X[C].
5806 // If X can be aliased then X[*] also aliases with *[C], *[*].
5807 CrossAlias(alias, kAnyInstanceAnyIndexAlias);
5808 AddAllRepresentatives(alias, kUnknownInstanceConstantIndexedAlias);
5809 }
5810 break;
5811
5812 case Place::kConstantIndexed: // Either X[C] or *[C] alias.
5813 if (alias->instance() == NULL) {
5814 // *[C] aliases with X[C], X[*], *[*].
5815 AddAllRepresentatives(alias, kAnyAllocationIndexedAlias);
5816 CrossAlias(alias, kAnyInstanceAnyIndexAlias);
5817 } else {
5818 // X[C] aliases with X[*].
5819 // If X can be aliased then X[C] also aliases with *[C], *[*].
5820 CrossAlias(alias, alias->CopyWithoutIndex());
5821 if (CanBeAliased(alias->instance())) {
5822 CrossAlias(alias, alias->CopyWithoutInstance());
5823 CrossAlias(alias, kAnyInstanceAnyIndexAlias);
5824 }
5825 }
5826 break;
5827
5828 case Place::kField:
5829 case Place::kVMField:
5830 if (CanBeAliased(alias->instance())) {
5831 // X.f or X.@offs alias with *.f and *.@offs respectively.
5832 CrossAlias(alias, alias->CopyWithoutInstance());
5833 }
5834 break;
5835
5836 case Place::kNone:
5837 UNREACHABLE();
5838 }
5839 }
5840
5841 // Returns true if the given load is unaffected by external side-effects.
5842 // This essentially means that no stores to the same location can
5843 // occur in other functions.
5844 bool IsIndependentFromEffects(Place* place) {
5845 if (place->IsFinalField()) {
5846 // Note that we can't use LoadField's is_immutable attribute here because
5847 // some VM-fields (those that have no corresponding Field object and
5848 // accessed through offset alone) can share offset but have different
5849 // immutability properties.
5850 // One example is the length property of growable and fixed size list. If
5851 // loads of these two properties occur in the same function for the same
5852 // receiver then they will get the same expression number. However
5853 // immutability of the length of fixed size list does not mean that
5854 // growable list also has immutable property. Thus we will make a
5855 // conservative assumption for the VM-properties.
5856 // TODO(vegorov): disambiguate immutable and non-immutable VM-fields with
5857 // the same offset e.g. through recognized kind.
5858 return true;
5859 }
5860
5861 return ((place->kind() == Place::kField) ||
5862 (place->kind() == Place::kVMField)) &&
5863 !CanBeAliased(place->instance());
5864 }
5865
5866 // Returns true if there are direct loads from the given place.
5867 bool HasLoadsFromPlace(Definition* defn, const Place* place) {
5868 ASSERT((place->kind() == Place::kField) ||
5869 (place->kind() == Place::kVMField));
5870
5871 for (Value* use = defn->input_use_list();
5872 use != NULL;
5873 use = use->next_use()) {
5874 Instruction* instr = use->instruction();
5875 if ((instr->IsRedefinition() ||
5876 instr->IsAssertAssignable()) &&
5877 HasLoadsFromPlace(instr->AsDefinition(), place)) {
5878 return true;
5879 }
5880 bool is_load = false, is_store;
5881 Place load_place(instr, &is_load, &is_store);
5882
5883 if (is_load && load_place.Equals(place)) {
5884 return true;
5885 }
5886 }
5887
5888 return false;
5889 }
5890
5891 // Check if any use of the definition can create an alias.
5892 // Can add more objects into aliasing_worklist_.
5893 bool AnyUseCreatesAlias(Definition* defn) {
5894 for (Value* use = defn->input_use_list();
5895 use != NULL;
5896 use = use->next_use()) {
5897 Instruction* instr = use->instruction();
5898 if (instr->IsPushArgument() ||
5899 (instr->IsStoreIndexed()
5900 && (use->use_index() == StoreIndexedInstr::kValuePos)) ||
5901 instr->IsStoreStaticField() ||
5902 instr->IsPhi()) {
5903 return true;
5904 } else if ((instr->IsAssertAssignable() || instr->IsRedefinition()) &&
5905 AnyUseCreatesAlias(instr->AsDefinition())) {
5906 return true;
5907 } else if ((instr->IsStoreInstanceField()
5908 && (use->use_index() != StoreInstanceFieldInstr::kInstancePos))) {
5909 ASSERT(use->use_index() == StoreInstanceFieldInstr::kValuePos);
5910 // If we store this value into an object that is not aliased itself
5911 // and we never load again then the store does not create an alias.
5912 StoreInstanceFieldInstr* store = instr->AsStoreInstanceField();
5913 Definition* instance =
5914 store->instance()->definition()->OriginalDefinition();
5915 if (Place::IsAllocation(instance) &&
5916 !instance->Identity().IsAliased()) {
5917 bool is_load, is_store;
5918 Place store_place(instr, &is_load, &is_store);
5919
5920 if (!HasLoadsFromPlace(instance, &store_place)) {
5921 // No loads found that match this store. If it is yet unknown if
5922 // the object is not aliased then optimistically assume this but
5923 // add it to the worklist to check its uses transitively.
5924 if (instance->Identity().IsUnknown()) {
5925 instance->SetIdentity(AliasIdentity::NotAliased());
5926 aliasing_worklist_.Add(instance);
5927 }
5928 continue;
5929 }
5930 }
5931 return true;
5932 }
5933 }
5934 return false;
5935 }
5936
5937 // Mark any value stored into the given object as potentially aliased.
5938 void MarkStoredValuesEscaping(Definition* defn) {
5939 // Find all stores into this object.
5940 for (Value* use = defn->input_use_list();
5941 use != NULL;
5942 use = use->next_use()) {
5943 if (use->instruction()->IsRedefinition() ||
5944 use->instruction()->IsAssertAssignable()) {
5945 MarkStoredValuesEscaping(use->instruction()->AsDefinition());
5946 continue;
5947 }
5948 if ((use->use_index() == StoreInstanceFieldInstr::kInstancePos) &&
5949 use->instruction()->IsStoreInstanceField()) {
5950 StoreInstanceFieldInstr* store =
5951 use->instruction()->AsStoreInstanceField();
5952 Definition* value = store->value()->definition()->OriginalDefinition();
5953 if (value->Identity().IsNotAliased()) {
5954 value->SetIdentity(AliasIdentity::Aliased());
5955 identity_rollback_.Add(value);
5956
5957 // Add to worklist to propagate the mark transitively.
5958 aliasing_worklist_.Add(value);
5959 }
5960 }
5961 }
5962 }
5963
5964 // Determine if the given definition can't be aliased.
5965 void ComputeAliasing(Definition* alloc) {
5966 ASSERT(Place::IsAllocation(alloc));
5967 ASSERT(alloc->Identity().IsUnknown());
5968 ASSERT(aliasing_worklist_.is_empty());
5969
5970 alloc->SetIdentity(AliasIdentity::NotAliased());
5971 aliasing_worklist_.Add(alloc);
5972
5973 while (!aliasing_worklist_.is_empty()) {
5974 Definition* defn = aliasing_worklist_.RemoveLast();
5975 ASSERT(Place::IsAllocation(defn));
5976 // If the definition in the worklist was optimistically marked as
5977 // not-aliased check that optimistic assumption still holds: check if
5978 // any of its uses can create an alias.
5979 if (!defn->Identity().IsAliased() && AnyUseCreatesAlias(defn)) {
5980 defn->SetIdentity(AliasIdentity::Aliased());
5981 identity_rollback_.Add(defn);
5982 }
5983
5984 // If the allocation site is marked as aliased conservatively mark
5985 // any values stored into the object aliased too.
5986 if (defn->Identity().IsAliased()) {
5987 MarkStoredValuesEscaping(defn);
5988 }
5989 }
5990 }
5991
5992 Isolate* isolate_;
5993
5994 DirectChainedHashMap<PointerKeyValueTrait<Place> >* places_map_;
5995
5996 const ZoneGrowableArray<Place*>& places_;
5997
5998 const PhiPlaceMoves* phi_moves_;
5999
6000 // A list of all seen aliases and a map that allows looking up canonical
6001 // alias object.
6002 GrowableArray<const Place*> aliases_;
6003 DirectChainedHashMap<PointerKeyValueTrait<const Place> > aliases_map_;
6004
6005 // Maps alias id to set of ids of places representing the alias.
6006 // Place represents an alias if this alias is least generic alias for
6007 // the place.
6008 // (see ToAlias for the definition of least generic alias).
6009 GrowableArray<BitVector*> representatives_;
6010
6011 // Maps alias id to set of ids of places aliased.
6012 GrowableArray<BitVector*> killed_;
6013
6014 // Set of ids of places that can be affected by side-effects other than
6015 // explicit stores (i.e. through calls).
6016 BitVector* aliased_by_effects_;
6017
6018 // Worklist used during alias analysis.
6019 GrowableArray<Definition*> aliasing_worklist_;
6020
6021 // List of definitions that had their identity set to Aliased. At the end
6022 // of load optimization their identity will be rolled back to Unknown to
6023 // avoid treating them as Aliased at later stages without checking first
6024 // as optimizations can potentially eliminate instructions leading to
6025 // aliasing.
6026 GrowableArray<Definition*> identity_rollback_;
6027 };
6028
6029
6030 static Definition* GetStoredValue(Instruction* instr) {
6031 if (instr->IsStoreIndexed()) {
6032 return instr->AsStoreIndexed()->value()->definition();
6033 }
6034
6035 StoreInstanceFieldInstr* store_instance_field = instr->AsStoreInstanceField();
6036 if (store_instance_field != NULL) {
6037 return store_instance_field->value()->definition();
6038 }
6039
6040 StoreStaticFieldInstr* store_static_field = instr->AsStoreStaticField();
6041 if (store_static_field != NULL) {
6042 return store_static_field->value()->definition();
6043 }
6044
6045 UNREACHABLE(); // Should only be called for supported store instructions.
6046 return NULL;
6047 }
6048
6049
6050 static bool IsPhiDependentPlace(Place* place) {
6051 return ((place->kind() == Place::kField) ||
6052 (place->kind() == Place::kVMField)) &&
6053 (place->instance() != NULL) &&
6054 place->instance()->IsPhi();
6055 }
6056
6057
6058 // For each place that depends on a phi ensure that equivalent places
6059 // corresponding to phi input are numbered and record outgoing phi moves
6060 // for each block which establish correspondence between phi dependent place
6061 // and phi input's place that is flowing in.
6062 static PhiPlaceMoves* ComputePhiMoves(
6063 DirectChainedHashMap<PointerKeyValueTrait<Place> >* map,
6064 ZoneGrowableArray<Place*>* places) {
6065 Isolate* isolate = Isolate::Current();
6066 PhiPlaceMoves* phi_moves = new(isolate) PhiPlaceMoves();
6067
6068 for (intptr_t i = 0; i < places->length(); i++) {
6069 Place* place = (*places)[i];
6070
6071 if (IsPhiDependentPlace(place)) {
6072 PhiInstr* phi = place->instance()->AsPhi();
6073 BlockEntryInstr* block = phi->GetBlock();
6074
6075 if (FLAG_trace_optimization) {
6076 OS::Print("phi dependent place %s\n", place->ToCString());
6077 }
6078
6079 Place input_place(*place);
6080 for (intptr_t j = 0; j < phi->InputCount(); j++) {
6081 input_place.set_instance(phi->InputAt(j)->definition());
6082
6083 Place* result = map->Lookup(&input_place);
6084 if (result == NULL) {
6085 result = Place::Wrap(isolate, input_place, places->length());
6086 map->Insert(result);
6087 places->Add(result);
6088 if (FLAG_trace_optimization) {
6089 OS::Print(" adding place %s as %" Pd "\n",
6090 result->ToCString(),
6091 result->id());
6092 }
6093 }
6094 phi_moves->CreateOutgoingMove(isolate,
6095 block->PredecessorAt(j),
6096 result->id(),
6097 place->id());
6098 }
6099 }
6100 }
6101
6102 return phi_moves;
6103 }
6104
6105
6106 enum CSEMode {
6107 kOptimizeLoads,
6108 kOptimizeStores
6109 };
6110
6111
6112 static AliasedSet* NumberPlaces(
6113 FlowGraph* graph,
6114 DirectChainedHashMap<PointerKeyValueTrait<Place> >* map,
6115 CSEMode mode) {
6116 // Loads representing different expression ids will be collected and
6117 // used to build per offset kill sets.
6118 Isolate* isolate = graph->isolate();
6119 ZoneGrowableArray<Place*>* places =
6120 new(isolate) ZoneGrowableArray<Place*>(10);
6121
6122 bool has_loads = false;
6123 bool has_stores = false;
6124 for (BlockIterator it = graph->reverse_postorder_iterator();
6125 !it.Done();
6126 it.Advance()) {
6127 BlockEntryInstr* block = it.Current();
6128
6129 for (ForwardInstructionIterator instr_it(block);
6130 !instr_it.Done();
6131 instr_it.Advance()) {
6132 Instruction* instr = instr_it.Current();
6133 Place place(instr, &has_loads, &has_stores);
6134 if (place.kind() == Place::kNone) {
6135 continue;
6136 }
6137
6138 Place* result = map->Lookup(&place);
6139 if (result == NULL) {
6140 result = Place::Wrap(isolate, place, places->length());
6141 map->Insert(result);
6142 places->Add(result);
6143
6144 if (FLAG_trace_optimization) {
6145 OS::Print("numbering %s as %" Pd "\n",
6146 result->ToCString(),
6147 result->id());
6148 }
6149 }
6150
6151 instr->set_place_id(result->id());
6152 }
6153 }
6154
6155 if ((mode == kOptimizeLoads) && !has_loads) {
6156 return NULL;
6157 }
6158 if ((mode == kOptimizeStores) && !has_stores) {
6159 return NULL;
6160 }
6161
6162 PhiPlaceMoves* phi_moves = ComputePhiMoves(map, places);
6163
6164 // Build aliasing sets mapping aliases to loads.
6165 return new(isolate) AliasedSet(isolate, map, places, phi_moves);
6166 }
6167
6168
6169 class LoadOptimizer : public ValueObject {
6170 public:
6171 LoadOptimizer(FlowGraph* graph, AliasedSet* aliased_set)
6172 : graph_(graph),
6173 aliased_set_(aliased_set),
6174 in_(graph_->preorder().length()),
6175 out_(graph_->preorder().length()),
6176 gen_(graph_->preorder().length()),
6177 kill_(graph_->preorder().length()),
6178 exposed_values_(graph_->preorder().length()),
6179 out_values_(graph_->preorder().length()),
6180 phis_(5),
6181 worklist_(5),
6182 congruency_worklist_(6),
6183 in_worklist_(NULL),
6184 forwarded_(false) {
6185 const intptr_t num_blocks = graph_->preorder().length();
6186 for (intptr_t i = 0; i < num_blocks; i++) {
6187 out_.Add(NULL);
6188 gen_.Add(new(I) BitVector(I, aliased_set_->max_place_id()));
6189 kill_.Add(new(I) BitVector(I, aliased_set_->max_place_id()));
6190 in_.Add(new(I) BitVector(I, aliased_set_->max_place_id()));
6191
6192 exposed_values_.Add(NULL);
6193 out_values_.Add(NULL);
6194 }
6195 }
6196
6197 ~LoadOptimizer() {
6198 aliased_set_->RollbackAliasedIdentites();
6199 }
6200
6201 Isolate* isolate() const { return graph_->isolate(); }
6202
6203 static bool OptimizeGraph(FlowGraph* graph) {
6204 ASSERT(FLAG_load_cse);
6205 if (FLAG_trace_load_optimization) {
6206 FlowGraphPrinter::PrintGraph("Before LoadOptimizer", graph);
6207 }
6208
6209 DirectChainedHashMap<PointerKeyValueTrait<Place> > map;
6210 AliasedSet* aliased_set = NumberPlaces(graph, &map, kOptimizeLoads);
6211 if ((aliased_set != NULL) && !aliased_set->IsEmpty()) {
6212 // If any loads were forwarded return true from Optimize to run load
6213 // forwarding again. This will allow to forward chains of loads.
6214 // This is especially important for context variables as they are built
6215 // as loads from loaded context.
6216 // TODO(vegorov): renumber newly discovered congruences during the
6217 // forwarding to forward chains without running whole pass twice.
6218 LoadOptimizer load_optimizer(graph, aliased_set);
6219 return load_optimizer.Optimize();
6220 }
6221 return false;
6222 }
6223
6224 private:
6225 bool Optimize() {
6226 ComputeInitialSets();
6227 ComputeOutSets();
6228 ComputeOutValues();
6229 if (graph_->is_licm_allowed()) {
6230 MarkLoopInvariantLoads();
6231 }
6232 ForwardLoads();
6233 EmitPhis();
6234
6235 if (FLAG_trace_load_optimization) {
6236 FlowGraphPrinter::PrintGraph("After LoadOptimizer", graph_);
6237 }
6238
6239 return forwarded_;
6240 }
6241
6242 // Compute sets of loads generated and killed by each block.
6243 // Additionally compute upwards exposed and generated loads for each block.
6244 // Exposed loads are those that can be replaced if a corresponding
6245 // reaching load will be found.
6246 // Loads that are locally redundant will be replaced as we go through
6247 // instructions.
6248 void ComputeInitialSets() {
6249 for (BlockIterator block_it = graph_->reverse_postorder_iterator();
6250 !block_it.Done();
6251 block_it.Advance()) {
6252 BlockEntryInstr* block = block_it.Current();
6253 const intptr_t preorder_number = block->preorder_number();
6254
6255 BitVector* kill = kill_[preorder_number];
6256 BitVector* gen = gen_[preorder_number];
6257
6258 ZoneGrowableArray<Definition*>* exposed_values = NULL;
6259 ZoneGrowableArray<Definition*>* out_values = NULL;
6260
6261 for (ForwardInstructionIterator instr_it(block);
6262 !instr_it.Done();
6263 instr_it.Advance()) {
6264 Instruction* instr = instr_it.Current();
6265
6266 bool is_load = false, is_store = false;
6267 Place place(instr, &is_load, &is_store);
6268
6269 BitVector* killed = NULL;
6270 if (is_store) {
6271 const intptr_t alias_id =
6272 aliased_set_->LookupAliasId(place.ToAlias());
6273 if (alias_id != AliasedSet::kNoAlias) {
6274 killed = aliased_set_->GetKilledSet(alias_id);
6275 } else if (!place.IsFinalField()) {
6276 // We encountered unknown alias: this means intrablock load
6277 // forwarding refined parameter of this store, for example
6278 //
6279 // o <- alloc()
6280 // a.f <- o
6281 // u <- a.f
6282 // u.x <- null ;; this store alias is *.x
6283 //
6284 // after intrablock load forwarding
6285 //
6286 // o <- alloc()
6287 // a.f <- o
6288 // o.x <- null ;; this store alias is o.x
6289 //
6290 // In this case we fallback to using place id recorded in the
6291 // instruction that still points to the old place with a more
6292 // generic alias.
6293 const intptr_t old_alias_id = aliased_set_->LookupAliasId(
6294 aliased_set_->places()[instr->place_id()]->ToAlias());
6295 killed = aliased_set_->GetKilledSet(old_alias_id);
6296 }
6297
6298 if (killed != NULL) {
6299 kill->AddAll(killed);
6300 // There is no need to clear out_values when clearing GEN set
6301 // because only those values that are in the GEN set
6302 // will ever be used.
6303 gen->RemoveAll(killed);
6304 }
6305
6306 // Only forward stores to normal arrays, float64, and simd arrays
6307 // to loads because other array stores (intXX/uintXX/float32)
6308 // may implicitly convert the value stored.
6309 StoreIndexedInstr* array_store = instr->AsStoreIndexed();
6310 if ((array_store == NULL) ||
6311 (array_store->class_id() == kArrayCid) ||
6312 (array_store->class_id() == kTypedDataFloat64ArrayCid) ||
6313 (array_store->class_id() == kTypedDataFloat32ArrayCid) ||
6314 (array_store->class_id() == kTypedDataFloat32x4ArrayCid)) {
6315 Place* canonical_place = aliased_set_->LookupCanonical(&place);
6316 if (canonical_place != NULL) {
6317 // Store has a corresponding numbered place that might have a
6318 // load. Try forwarding stored value to it.
6319 gen->Add(canonical_place->id());
6320 if (out_values == NULL) out_values = CreateBlockOutValues();
6321 (*out_values)[canonical_place->id()] = GetStoredValue(instr);
6322 }
6323 }
6324
6325 ASSERT(!instr->IsDefinition() ||
6326 !IsLoadEliminationCandidate(instr->AsDefinition()));
6327 continue;
6328 } else if (is_load) {
6329 // Check if this load needs renumbering because of the intrablock
6330 // load forwarding.
6331 const Place* canonical = aliased_set_->LookupCanonical(&place);
6332 if ((canonical != NULL) &&
6333 (canonical->id() != instr->AsDefinition()->place_id())) {
6334 instr->AsDefinition()->set_place_id(canonical->id());
6335 }
6336 }
6337
6338 // If instruction has effects then kill all loads affected.
6339 if (!instr->Effects().IsNone()) {
6340 kill->AddAll(aliased_set_->aliased_by_effects());
6341 // There is no need to clear out_values when removing values from GEN
6342 // set because only those values that are in the GEN set
6343 // will ever be used.
6344 gen->RemoveAll(aliased_set_->aliased_by_effects());
6345 continue;
6346 }
6347
6348 Definition* defn = instr->AsDefinition();
6349 if (defn == NULL) {
6350 continue;
6351 }
6352
6353 // For object allocation forward initial values of the fields to
6354 // subsequent loads. For skip final fields. Final fields are
6355 // initialized in constructor that potentially can be not inlined into
6356 // the function that we are currently optimizing. However at the same
6357 // time we assume that values of the final fields can be forwarded
6358 // across side-effects. If we add 'null' as known values for these
6359 // fields here we will incorrectly propagate this null across
6360 // constructor invocation.
6361 AllocateObjectInstr* alloc = instr->AsAllocateObject();
6362 if ((alloc != NULL)) {
6363 for (Value* use = alloc->input_use_list();
6364 use != NULL;
6365 use = use->next_use()) {
6366 // Look for all immediate loads from this object.
6367 if (use->use_index() != 0) {
6368 continue;
6369 }
6370
6371 LoadFieldInstr* load = use->instruction()->AsLoadField();
6372 if (load != NULL) {
6373 // Found a load. Initialize current value of the field to null for
6374 // normal fields, or with type arguments.
6375
6376 // Forward for all fields for non-escaping objects and only
6377 // non-final fields and type arguments for escaping ones.
6378 if (aliased_set_->CanBeAliased(alloc) &&
6379 (load->field() != NULL) &&
6380 load->field()->is_final()) {
6381 continue;
6382 }
6383
6384 Definition* forward_def = graph_->constant_null();
6385 if (alloc->ArgumentCount() > 0) {
6386 ASSERT(alloc->ArgumentCount() == 1);
6387 intptr_t type_args_offset =
6388 alloc->cls().type_arguments_field_offset();
6389 if (load->offset_in_bytes() == type_args_offset) {
6390 forward_def = alloc->PushArgumentAt(0)->value()->definition();
6391 }
6392 }
6393 gen->Add(load->place_id());
6394 if (out_values == NULL) out_values = CreateBlockOutValues();
6395 (*out_values)[load->place_id()] = forward_def;
6396 }
6397 }
6398 continue;
6399 }
6400
6401 if (!IsLoadEliminationCandidate(defn)) {
6402 continue;
6403 }
6404
6405 const intptr_t place_id = defn->place_id();
6406 if (gen->Contains(place_id)) {
6407 // This is a locally redundant load.
6408 ASSERT((out_values != NULL) && ((*out_values)[place_id] != NULL));
6409
6410 Definition* replacement = (*out_values)[place_id];
6411 EnsureSSATempIndex(graph_, defn, replacement);
6412 if (FLAG_trace_optimization) {
6413 OS::Print("Replacing load v%" Pd " with v%" Pd "\n",
6414 defn->ssa_temp_index(),
6415 replacement->ssa_temp_index());
6416 }
6417
6418 defn->ReplaceUsesWith(replacement);
6419 instr_it.RemoveCurrentFromGraph();
6420 forwarded_ = true;
6421 continue;
6422 } else if (!kill->Contains(place_id)) {
6423 // This is an exposed load: it is the first representative of a
6424 // given expression id and it is not killed on the path from
6425 // the block entry.
6426 if (exposed_values == NULL) {
6427 static const intptr_t kMaxExposedValuesInitialSize = 5;
6428 exposed_values = new(I) ZoneGrowableArray<Definition*>(
6429 Utils::Minimum(kMaxExposedValuesInitialSize,
6430 aliased_set_->max_place_id()));
6431 }
6432
6433 exposed_values->Add(defn);
6434 }
6435
6436 gen->Add(place_id);
6437
6438 if (out_values == NULL) out_values = CreateBlockOutValues();
6439 (*out_values)[place_id] = defn;
6440 }
6441
6442 exposed_values_[preorder_number] = exposed_values;
6443 out_values_[preorder_number] = out_values;
6444 }
6445 }
6446
6447 static void PerformPhiMoves(PhiPlaceMoves::MovesList phi_moves,
6448 BitVector* out,
6449 BitVector* forwarded_loads) {
6450 forwarded_loads->Clear();
6451
6452 for (intptr_t i = 0; i < phi_moves->length(); i++) {
6453 const intptr_t from = (*phi_moves)[i].from();
6454 const intptr_t to = (*phi_moves)[i].to();
6455 if (from == to) continue;
6456
6457 if (out->Contains(from)) {
6458 forwarded_loads->Add(to);
6459 }
6460 }
6461
6462 for (intptr_t i = 0; i < phi_moves->length(); i++) {
6463 const intptr_t from = (*phi_moves)[i].from();
6464 const intptr_t to = (*phi_moves)[i].to();
6465 if (from == to) continue;
6466
6467 out->Remove(to);
6468 }
6469
6470 out->AddAll(forwarded_loads);
6471 }
6472
6473 // Compute OUT sets by propagating them iteratively until fix point
6474 // is reached.
6475 void ComputeOutSets() {
6476 BitVector* temp = new(I) BitVector(I, aliased_set_->max_place_id());
6477 BitVector* forwarded_loads =
6478 new(I) BitVector(I, aliased_set_->max_place_id());
6479 BitVector* temp_out = new(I) BitVector(I, aliased_set_->max_place_id());
6480
6481 bool changed = true;
6482 while (changed) {
6483 changed = false;
6484
6485 for (BlockIterator block_it = graph_->reverse_postorder_iterator();
6486 !block_it.Done();
6487 block_it.Advance()) {
6488 BlockEntryInstr* block = block_it.Current();
6489
6490 const intptr_t preorder_number = block->preorder_number();
6491
6492 BitVector* block_in = in_[preorder_number];
6493 BitVector* block_out = out_[preorder_number];
6494 BitVector* block_kill = kill_[preorder_number];
6495 BitVector* block_gen = gen_[preorder_number];
6496
6497 // Compute block_in as the intersection of all out(p) where p
6498 // is a predecessor of the current block.
6499 if (block->IsGraphEntry()) {
6500 temp->Clear();
6501 } else {
6502 temp->SetAll();
6503 ASSERT(block->PredecessorCount() > 0);
6504 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
6505 BlockEntryInstr* pred = block->PredecessorAt(i);
6506 BitVector* pred_out = out_[pred->preorder_number()];
6507 if (pred_out == NULL) continue;
6508 PhiPlaceMoves::MovesList phi_moves =
6509 aliased_set_->phi_moves()->GetOutgoingMoves(pred);
6510 if (phi_moves != NULL) {
6511 // If there are phi moves, perform intersection with
6512 // a copy of pred_out where the phi moves are applied.
6513 temp_out->CopyFrom(pred_out);
6514 PerformPhiMoves(phi_moves, temp_out, forwarded_loads);
6515 pred_out = temp_out;
6516 }
6517 temp->Intersect(pred_out);
6518 }
6519 }
6520
6521 if (!temp->Equals(*block_in) || (block_out == NULL)) {
6522 // If IN set has changed propagate the change to OUT set.
6523 block_in->CopyFrom(temp);
6524
6525 temp->RemoveAll(block_kill);
6526 temp->AddAll(block_gen);
6527
6528 if ((block_out == NULL) || !block_out->Equals(*temp)) {
6529 if (block_out == NULL) {
6530 block_out = out_[preorder_number] =
6531 new(I) BitVector(I, aliased_set_->max_place_id());
6532 }
6533 block_out->CopyFrom(temp);
6534 changed = true;
6535 }
6536 }
6537 }
6538 }
6539 }
6540
6541 // Compute out_values mappings by propagating them in reverse postorder once
6542 // through the graph. Generate phis on back edges where eager merge is
6543 // impossible.
6544 // No replacement is done at this point and thus any out_value[place_id] is
6545 // changed at most once: from NULL to an actual value.
6546 // When merging incoming loads we might need to create a phi.
6547 // These phis are not inserted at the graph immediately because some of them
6548 // might become redundant after load forwarding is done.
6549 void ComputeOutValues() {
6550 GrowableArray<PhiInstr*> pending_phis(5);
6551 ZoneGrowableArray<Definition*>* temp_forwarded_values = NULL;
6552
6553 for (BlockIterator block_it = graph_->reverse_postorder_iterator();
6554 !block_it.Done();
6555 block_it.Advance()) {
6556 BlockEntryInstr* block = block_it.Current();
6557
6558 const bool can_merge_eagerly = CanMergeEagerly(block);
6559
6560 const intptr_t preorder_number = block->preorder_number();
6561
6562 ZoneGrowableArray<Definition*>* block_out_values =
6563 out_values_[preorder_number];
6564
6565
6566 // If OUT set has changed then we have new values available out of
6567 // the block. Compute these values creating phi where necessary.
6568 for (BitVector::Iterator it(out_[preorder_number]);
6569 !it.Done();
6570 it.Advance()) {
6571 const intptr_t place_id = it.Current();
6572
6573 if (block_out_values == NULL) {
6574 out_values_[preorder_number] = block_out_values =
6575 CreateBlockOutValues();
6576 }
6577
6578 if ((*block_out_values)[place_id] == NULL) {
6579 ASSERT(block->PredecessorCount() > 0);
6580 Definition* in_value = can_merge_eagerly ?
6581 MergeIncomingValues(block, place_id) : NULL;
6582 if ((in_value == NULL) &&
6583 (in_[preorder_number]->Contains(place_id))) {
6584 PhiInstr* phi = new(I) PhiInstr(block->AsJoinEntry(),
6585 block->PredecessorCount());
6586 phi->set_place_id(place_id);
6587 pending_phis.Add(phi);
6588 in_value = phi;
6589 }
6590 (*block_out_values)[place_id] = in_value;
6591 }
6592 }
6593
6594 // If the block has outgoing phi moves perform them. Use temporary list
6595 // of values to ensure that cyclic moves are performed correctly.
6596 PhiPlaceMoves::MovesList phi_moves =
6597 aliased_set_->phi_moves()->GetOutgoingMoves(block);
6598 if ((phi_moves != NULL) && (block_out_values != NULL)) {
6599 if (temp_forwarded_values == NULL) {
6600 temp_forwarded_values = CreateBlockOutValues();
6601 }
6602
6603 for (intptr_t i = 0; i < phi_moves->length(); i++) {
6604 const intptr_t from = (*phi_moves)[i].from();
6605 const intptr_t to = (*phi_moves)[i].to();
6606 if (from == to) continue;
6607
6608 (*temp_forwarded_values)[to] = (*block_out_values)[from];
6609 }
6610
6611 for (intptr_t i = 0; i < phi_moves->length(); i++) {
6612 const intptr_t from = (*phi_moves)[i].from();
6613 const intptr_t to = (*phi_moves)[i].to();
6614 if (from == to) continue;
6615
6616 (*block_out_values)[to] = (*temp_forwarded_values)[to];
6617 }
6618 }
6619
6620 if (FLAG_trace_load_optimization) {
6621 OS::Print("B%" Pd "\n", block->block_id());
6622 OS::Print(" IN: ");
6623 aliased_set_->PrintSet(in_[preorder_number]);
6624 OS::Print("\n");
6625
6626 OS::Print(" KILL: ");
6627 aliased_set_->PrintSet(kill_[preorder_number]);
6628 OS::Print("\n");
6629
6630 OS::Print(" OUT: ");
6631 aliased_set_->PrintSet(out_[preorder_number]);
6632 OS::Print("\n");
6633 }
6634 }
6635
6636 // All blocks were visited. Fill pending phis with inputs
6637 // that flow on back edges.
6638 for (intptr_t i = 0; i < pending_phis.length(); i++) {
6639 FillPhiInputs(pending_phis[i]);
6640 }
6641 }
6642
6643 bool CanMergeEagerly(BlockEntryInstr* block) {
6644 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
6645 BlockEntryInstr* pred = block->PredecessorAt(i);
6646 if (pred->postorder_number() < block->postorder_number()) {
6647 return false;
6648 }
6649 }
6650 return true;
6651 }
6652
6653 void MarkLoopInvariantLoads() {
6654 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers =
6655 graph_->LoopHeaders();
6656
6657 ZoneGrowableArray<BitVector*>* invariant_loads =
6658 new(I) ZoneGrowableArray<BitVector*>(loop_headers.length());
6659
6660 for (intptr_t i = 0; i < loop_headers.length(); i++) {
6661 BlockEntryInstr* header = loop_headers[i];
6662 BlockEntryInstr* pre_header = header->ImmediateDominator();
6663 if (pre_header == NULL) {
6664 invariant_loads->Add(NULL);
6665 continue;
6666 }
6667
6668 BitVector* loop_gen = new(I) BitVector(I, aliased_set_->max_place_id());
6669 for (BitVector::Iterator loop_it(header->loop_info());
6670 !loop_it.Done();
6671 loop_it.Advance()) {
6672 const intptr_t preorder_number = loop_it.Current();
6673 loop_gen->AddAll(gen_[preorder_number]);
6674 }
6675
6676 for (BitVector::Iterator loop_it(header->loop_info());
6677 !loop_it.Done();
6678 loop_it.Advance()) {
6679 const intptr_t preorder_number = loop_it.Current();
6680 loop_gen->RemoveAll(kill_[preorder_number]);
6681 }
6682
6683 if (FLAG_trace_optimization) {
6684 for (BitVector::Iterator it(loop_gen); !it.Done(); it.Advance()) {
6685 OS::Print("place %s is loop invariant for B%" Pd "\n",
6686 aliased_set_->places()[it.Current()]->ToCString(),
6687 header->block_id());
6688 }
6689 }
6690
6691 invariant_loads->Add(loop_gen);
6692 }
6693
6694 graph_->set_loop_invariant_loads(invariant_loads);
6695 }
6696
6697 // Compute incoming value for the given expression id.
6698 // Will create a phi if different values are incoming from multiple
6699 // predecessors.
6700 Definition* MergeIncomingValues(BlockEntryInstr* block, intptr_t place_id) {
6701 // First check if the same value is coming in from all predecessors.
6702 static Definition* const kDifferentValuesMarker =
6703 reinterpret_cast<Definition*>(-1);
6704 Definition* incoming = NULL;
6705 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
6706 BlockEntryInstr* pred = block->PredecessorAt(i);
6707 ZoneGrowableArray<Definition*>* pred_out_values =
6708 out_values_[pred->preorder_number()];
6709 if ((pred_out_values == NULL) || ((*pred_out_values)[place_id] == NULL)) {
6710 return NULL;
6711 } else if (incoming == NULL) {
6712 incoming = (*pred_out_values)[place_id];
6713 } else if (incoming != (*pred_out_values)[place_id]) {
6714 incoming = kDifferentValuesMarker;
6715 }
6716 }
6717
6718 if (incoming != kDifferentValuesMarker) {
6719 ASSERT(incoming != NULL);
6720 return incoming;
6721 }
6722
6723 // Incoming values are different. Phi is required to merge.
6724 PhiInstr* phi = new(I) PhiInstr(
6725 block->AsJoinEntry(), block->PredecessorCount());
6726 phi->set_place_id(place_id);
6727 FillPhiInputs(phi);
6728 return phi;
6729 }
6730
6731 void FillPhiInputs(PhiInstr* phi) {
6732 BlockEntryInstr* block = phi->GetBlock();
6733 const intptr_t place_id = phi->place_id();
6734
6735 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
6736 BlockEntryInstr* pred = block->PredecessorAt(i);
6737 ZoneGrowableArray<Definition*>* pred_out_values =
6738 out_values_[pred->preorder_number()];
6739 ASSERT((*pred_out_values)[place_id] != NULL);
6740
6741 // Sets of outgoing values are not linked into use lists so
6742 // they might contain values that were replaced and removed
6743 // from the graph by this iteration.
6744 // To prevent using them we additionally mark definitions themselves
6745 // as replaced and store a pointer to the replacement.
6746 Definition* replacement = (*pred_out_values)[place_id]->Replacement();
6747 Value* input = new(I) Value(replacement);
6748 phi->SetInputAt(i, input);
6749 replacement->AddInputUse(input);
6750 }
6751
6752 phi->set_ssa_temp_index(graph_->alloc_ssa_temp_index());
6753 phis_.Add(phi); // Postpone phi insertion until after load forwarding.
6754
6755 if (FLAG_trace_load_optimization) {
6756 OS::Print("created pending phi %s for %s at B%" Pd "\n",
6757 phi->ToCString(),
6758 aliased_set_->places()[place_id]->ToCString(),
6759 block->block_id());
6760 }
6761 }
6762
6763 // Iterate over basic blocks and replace exposed loads with incoming
6764 // values.
6765 void ForwardLoads() {
6766 for (BlockIterator block_it = graph_->reverse_postorder_iterator();
6767 !block_it.Done();
6768 block_it.Advance()) {
6769 BlockEntryInstr* block = block_it.Current();
6770
6771 ZoneGrowableArray<Definition*>* loads =
6772 exposed_values_[block->preorder_number()];
6773 if (loads == NULL) continue; // No exposed loads.
6774
6775 BitVector* in = in_[block->preorder_number()];
6776
6777 for (intptr_t i = 0; i < loads->length(); i++) {
6778 Definition* load = (*loads)[i];
6779 if (!in->Contains(load->place_id())) continue; // No incoming value.
6780
6781 Definition* replacement = MergeIncomingValues(block, load->place_id());
6782 ASSERT(replacement != NULL);
6783
6784 // Sets of outgoing values are not linked into use lists so
6785 // they might contain values that were replace and removed
6786 // from the graph by this iteration.
6787 // To prevent using them we additionally mark definitions themselves
6788 // as replaced and store a pointer to the replacement.
6789 replacement = replacement->Replacement();
6790
6791 if (load != replacement) {
6792 EnsureSSATempIndex(graph_, load, replacement);
6793
6794 if (FLAG_trace_optimization) {
6795 OS::Print("Replacing load v%" Pd " with v%" Pd "\n",
6796 load->ssa_temp_index(),
6797 replacement->ssa_temp_index());
6798 }
6799
6800 load->ReplaceUsesWith(replacement);
6801 load->RemoveFromGraph();
6802 load->SetReplacement(replacement);
6803 forwarded_ = true;
6804 }
6805 }
6806 }
6807 }
6808
6809 // Check if the given phi take the same value on all code paths.
6810 // Eliminate it as redundant if this is the case.
6811 // When analyzing phi operands assumes that only generated during
6812 // this load phase can be redundant. They can be distinguished because
6813 // they are not marked alive.
6814 // TODO(vegorov): move this into a separate phase over all phis.
6815 bool EliminateRedundantPhi(PhiInstr* phi) {
6816 Definition* value = NULL; // Possible value of this phi.
6817
6818 worklist_.Clear();
6819 if (in_worklist_ == NULL) {
6820 in_worklist_ = new(I) BitVector(I, graph_->current_ssa_temp_index());
6821 } else {
6822 in_worklist_->Clear();
6823 }
6824
6825 worklist_.Add(phi);
6826 in_worklist_->Add(phi->ssa_temp_index());
6827
6828 for (intptr_t i = 0; i < worklist_.length(); i++) {
6829 PhiInstr* phi = worklist_[i];
6830
6831 for (intptr_t i = 0; i < phi->InputCount(); i++) {
6832 Definition* input = phi->InputAt(i)->definition();
6833 if (input == phi) continue;
6834
6835 PhiInstr* phi_input = input->AsPhi();
6836 if ((phi_input != NULL) && !phi_input->is_alive()) {
6837 if (!in_worklist_->Contains(phi_input->ssa_temp_index())) {
6838 worklist_.Add(phi_input);
6839 in_worklist_->Add(phi_input->ssa_temp_index());
6840 }
6841 continue;
6842 }
6843
6844 if (value == NULL) {
6845 value = input;
6846 } else if (value != input) {
6847 return false; // This phi is not redundant.
6848 }
6849 }
6850 }
6851
6852 // All phis in the worklist are redundant and have the same computed
6853 // value on all code paths.
6854 ASSERT(value != NULL);
6855 for (intptr_t i = 0; i < worklist_.length(); i++) {
6856 worklist_[i]->ReplaceUsesWith(value);
6857 }
6858
6859 return true;
6860 }
6861
6862 // Returns true if definitions are congruent assuming their inputs
6863 // are congruent.
6864 bool CanBeCongruent(Definition* a, Definition* b) {
6865 return (a->tag() == b->tag()) &&
6866 ((a->IsPhi() && (a->GetBlock() == b->GetBlock())) ||
6867 (a->AllowsCSE() && a->Dependencies().IsNone() &&
6868 a->AttributesEqual(b)));
6869 }
6870
6871 // Given two definitions check if they are congruent under assumption that
6872 // their inputs will be proven congruent. If they are - add them to the
6873 // worklist to check their inputs' congruency.
6874 // Returns true if pair was added to the worklist or is already in the
6875 // worklist and false if a and b are not congruent.
6876 bool AddPairToCongruencyWorklist(Definition* a, Definition* b) {
6877 if (!CanBeCongruent(a, b)) {
6878 return false;
6879 }
6880
6881 // If a is already in the worklist check if it is being compared to b.
6882 // Give up if it is not.
6883 if (in_worklist_->Contains(a->ssa_temp_index())) {
6884 for (intptr_t i = 0; i < congruency_worklist_.length(); i += 2) {
6885 if (a == congruency_worklist_[i]) {
6886 return (b == congruency_worklist_[i + 1]);
6887 }
6888 }
6889 UNREACHABLE();
6890 } else if (in_worklist_->Contains(b->ssa_temp_index())) {
6891 return AddPairToCongruencyWorklist(b, a);
6892 }
6893
6894 congruency_worklist_.Add(a);
6895 congruency_worklist_.Add(b);
6896 in_worklist_->Add(a->ssa_temp_index());
6897 return true;
6898 }
6899
6900 bool AreInputsCongruent(Definition* a, Definition* b) {
6901 ASSERT(a->tag() == b->tag());
6902 ASSERT(a->InputCount() == b->InputCount());
6903 for (intptr_t j = 0; j < a->InputCount(); j++) {
6904 Definition* inputA = a->InputAt(j)->definition();
6905 Definition* inputB = b->InputAt(j)->definition();
6906
6907 if (inputA != inputB) {
6908 if (!AddPairToCongruencyWorklist(inputA, inputB)) {
6909 return false;
6910 }
6911 }
6912 }
6913 return true;
6914 }
6915
6916 // Returns true if instruction dom dominates instruction other.
6917 static bool Dominates(Instruction* dom, Instruction* other) {
6918 BlockEntryInstr* dom_block = dom->GetBlock();
6919 BlockEntryInstr* other_block = other->GetBlock();
6920
6921 if (dom_block == other_block) {
6922 for (Instruction* current = dom->next();
6923 current != NULL;
6924 current = current->next()) {
6925 if (current == other) {
6926 return true;
6927 }
6928 }
6929 return false;
6930 }
6931
6932 return dom_block->Dominates(other_block);
6933 }
6934
6935 // Replace the given phi with another if they are congruent.
6936 // Returns true if succeeds.
6937 bool ReplacePhiWith(PhiInstr* phi, PhiInstr* replacement) {
6938 ASSERT(phi->InputCount() == replacement->InputCount());
6939 ASSERT(phi->block() == replacement->block());
6940
6941 congruency_worklist_.Clear();
6942 if (in_worklist_ == NULL) {
6943 in_worklist_ = new(I) BitVector(I, graph_->current_ssa_temp_index());
6944 } else {
6945 in_worklist_->Clear();
6946 }
6947
6948 // During the comparison worklist contains pairs of definitions to be
6949 // compared.
6950 if (!AddPairToCongruencyWorklist(phi, replacement)) {
6951 return false;
6952 }
6953
6954 // Process the worklist. It might grow during each comparison step.
6955 for (intptr_t i = 0; i < congruency_worklist_.length(); i += 2) {
6956 if (!AreInputsCongruent(congruency_worklist_[i],
6957 congruency_worklist_[i + 1])) {
6958 return false;
6959 }
6960 }
6961
6962 // At this point worklist contains pairs of congruent definitions.
6963 // Replace the one member of the pair with another maintaining proper
6964 // domination relation between definitions and uses.
6965 for (intptr_t i = 0; i < congruency_worklist_.length(); i += 2) {
6966 Definition* a = congruency_worklist_[i];
6967 Definition* b = congruency_worklist_[i + 1];
6968
6969 // If these definitions are not phis then we need to pick up one
6970 // that dominates another as the replacement: if a dominates b swap them.
6971 // Note: both a and b are used as a phi input at the same block B which
6972 // means a dominates B and b dominates B, which guarantees that either
6973 // a dominates b or b dominates a.
6974 if (!a->IsPhi()) {
6975 if (Dominates(a, b)) {
6976 Definition* t = a;
6977 a = b;
6978 b = t;
6979 }
6980 ASSERT(Dominates(b, a));
6981 }
6982
6983 if (FLAG_trace_load_optimization) {
6984 OS::Print("Replacing %s with congruent %s\n",
6985 a->ToCString(),
6986 b->ToCString());
6987 }
6988
6989 a->ReplaceUsesWith(b);
6990 if (a->IsPhi()) {
6991 // We might be replacing a phi introduced by the load forwarding
6992 // that is not inserted in the graph yet.
6993 ASSERT(b->IsPhi());
6994 PhiInstr* phi_a = a->AsPhi();
6995 if (phi_a->is_alive()) {
6996 phi_a->mark_dead();
6997 phi_a->block()->RemovePhi(phi_a);
6998 phi_a->UnuseAllInputs();
6999 }
7000 } else {
7001 a->RemoveFromGraph();
7002 }
7003 }
7004
7005 return true;
7006 }
7007
7008 // Insert the given phi into the graph. Attempt to find an equal one in the
7009 // target block first.
7010 // Returns true if the phi was inserted and false if it was replaced.
7011 bool EmitPhi(PhiInstr* phi) {
7012 for (PhiIterator it(phi->block()); !it.Done(); it.Advance()) {
7013 if (ReplacePhiWith(phi, it.Current())) {
7014 return false;
7015 }
7016 }
7017
7018 phi->mark_alive();
7019 phi->block()->InsertPhi(phi);
7020 return true;
7021 }
7022
7023 // Phis have not yet been inserted into the graph but they have uses of
7024 // their inputs. Insert the non-redundant ones and clear the input uses
7025 // of the redundant ones.
7026 void EmitPhis() {
7027 // First eliminate all redundant phis.
7028 for (intptr_t i = 0; i < phis_.length(); i++) {
7029 PhiInstr* phi = phis_[i];
7030 if (!phi->HasUses() || EliminateRedundantPhi(phi)) {
7031 phi->UnuseAllInputs();
7032 phis_[i] = NULL;
7033 }
7034 }
7035
7036 // Now emit phis or replace them with equal phis already present in the
7037 // graph.
7038 for (intptr_t i = 0; i < phis_.length(); i++) {
7039 PhiInstr* phi = phis_[i];
7040 if ((phi != NULL) && (!phi->HasUses() || !EmitPhi(phi))) {
7041 phi->UnuseAllInputs();
7042 }
7043 }
7044 }
7045
7046 ZoneGrowableArray<Definition*>* CreateBlockOutValues() {
7047 ZoneGrowableArray<Definition*>* out =
7048 new(I) ZoneGrowableArray<Definition*>(aliased_set_->max_place_id());
7049 for (intptr_t i = 0; i < aliased_set_->max_place_id(); i++) {
7050 out->Add(NULL);
7051 }
7052 return out;
7053 }
7054
7055 FlowGraph* graph_;
7056 DirectChainedHashMap<PointerKeyValueTrait<Place> >* map_;
7057
7058 // Mapping between field offsets in words and expression ids of loads from
7059 // that offset.
7060 AliasedSet* aliased_set_;
7061
7062 // Per block sets of expression ids for loads that are: incoming (available
7063 // on the entry), outgoing (available on the exit), generated and killed.
7064 GrowableArray<BitVector*> in_;
7065 GrowableArray<BitVector*> out_;
7066 GrowableArray<BitVector*> gen_;
7067 GrowableArray<BitVector*> kill_;
7068
7069 // Per block list of upwards exposed loads.
7070 GrowableArray<ZoneGrowableArray<Definition*>*> exposed_values_;
7071
7072 // Per block mappings between expression ids and outgoing definitions that
7073 // represent those ids.
7074 GrowableArray<ZoneGrowableArray<Definition*>*> out_values_;
7075
7076 // List of phis generated during ComputeOutValues and ForwardLoads.
7077 // Some of these phis might be redundant and thus a separate pass is
7078 // needed to emit only non-redundant ones.
7079 GrowableArray<PhiInstr*> phis_;
7080
7081 // Auxiliary worklist used by redundant phi elimination.
7082 GrowableArray<PhiInstr*> worklist_;
7083 GrowableArray<Definition*> congruency_worklist_;
7084 BitVector* in_worklist_;
7085
7086
7087 // True if any load was eliminated.
7088 bool forwarded_;
7089
7090 DISALLOW_COPY_AND_ASSIGN(LoadOptimizer);
7091 };
7092
7093
7094 class StoreOptimizer : public LivenessAnalysis {
7095 public:
7096 StoreOptimizer(FlowGraph* graph,
7097 AliasedSet* aliased_set,
7098 DirectChainedHashMap<PointerKeyValueTrait<Place> >* map)
7099 : LivenessAnalysis(aliased_set->max_place_id(), graph->postorder()),
7100 graph_(graph),
7101 map_(map),
7102 aliased_set_(aliased_set),
7103 exposed_stores_(graph_->postorder().length()) {
7104 const intptr_t num_blocks = graph_->postorder().length();
7105 for (intptr_t i = 0; i < num_blocks; i++) {
7106 exposed_stores_.Add(NULL);
7107 }
7108 }
7109
7110 static void OptimizeGraph(FlowGraph* graph) {
7111 ASSERT(FLAG_load_cse);
7112 if (FLAG_trace_load_optimization) {
7113 FlowGraphPrinter::PrintGraph("Before StoreOptimizer", graph);
7114 }
7115
7116 DirectChainedHashMap<PointerKeyValueTrait<Place> > map;
7117 AliasedSet* aliased_set = NumberPlaces(graph, &map, kOptimizeStores);
7118 if ((aliased_set != NULL) && !aliased_set->IsEmpty()) {
7119 StoreOptimizer store_optimizer(graph, aliased_set, &map);
7120 store_optimizer.Optimize();
7121 }
7122 }
7123
7124 private:
7125 void Optimize() {
7126 Analyze();
7127 if (FLAG_trace_load_optimization) {
7128 Dump();
7129 }
7130 EliminateDeadStores();
7131 if (FLAG_trace_load_optimization) {
7132 FlowGraphPrinter::PrintGraph("After StoreOptimizer", graph_);
7133 }
7134 }
7135
7136 bool CanEliminateStore(Instruction* instr) {
7137 switch (instr->tag()) {
7138 case Instruction::kStoreInstanceField:
7139 if (instr->AsStoreInstanceField()->is_initialization()) {
7140 // Can't eliminate stores that initialized unboxed fields.
7141 return false;
7142 }
7143 case Instruction::kStoreIndexed:
7144 case Instruction::kStoreStaticField:
7145 return true;
7146 default:
7147 UNREACHABLE();
7148 return false;
7149 }
7150 }
7151
7152 virtual void ComputeInitialSets() {
7153 Isolate* isolate = graph_->isolate();
7154 BitVector* all_places = new(isolate) BitVector(isolate,
7155 aliased_set_->max_place_id());
7156 all_places->SetAll();
7157 for (BlockIterator block_it = graph_->postorder_iterator();
7158 !block_it.Done();
7159 block_it.Advance()) {
7160 BlockEntryInstr* block = block_it.Current();
7161 const intptr_t postorder_number = block->postorder_number();
7162
7163 BitVector* kill = kill_[postorder_number];
7164 BitVector* live_in = live_in_[postorder_number];
7165 BitVector* live_out = live_out_[postorder_number];
7166
7167 ZoneGrowableArray<Instruction*>* exposed_stores = NULL;
7168
7169 // Iterate backwards starting at the last instruction.
7170 for (BackwardInstructionIterator instr_it(block);
7171 !instr_it.Done();
7172 instr_it.Advance()) {
7173 Instruction* instr = instr_it.Current();
7174
7175 bool is_load = false;
7176 bool is_store = false;
7177 Place place(instr, &is_load, &is_store);
7178 if (place.IsFinalField()) {
7179 // Loads/stores of final fields do not participate.
7180 continue;
7181 }
7182
7183 // Handle stores.
7184 if (is_store) {
7185 if (kill->Contains(instr->place_id())) {
7186 if (!live_in->Contains(instr->place_id()) &&
7187 CanEliminateStore(instr)) {
7188 if (FLAG_trace_optimization) {
7189 OS::Print(
7190 "Removing dead store to place %" Pd " in block B%" Pd "\n",
7191 instr->place_id(), block->block_id());
7192 }
7193 instr_it.RemoveCurrentFromGraph();
7194 }
7195 } else if (!live_in->Contains(instr->place_id())) {
7196 // Mark this store as down-ward exposed: They are the only
7197 // candidates for the global store elimination.
7198 if (exposed_stores == NULL) {
7199 const intptr_t kMaxExposedStoresInitialSize = 5;
7200 exposed_stores = new(isolate) ZoneGrowableArray<Instruction*>(
7201 Utils::Minimum(kMaxExposedStoresInitialSize,
7202 aliased_set_->max_place_id()));
7203 }
7204 exposed_stores->Add(instr);
7205 }
7206 // Interfering stores kill only loads from the same place.
7207 kill->Add(instr->place_id());
7208 live_in->Remove(instr->place_id());
7209 continue;
7210 }
7211
7212 // Handle side effects, deoptimization and function return.
7213 if (!instr->Effects().IsNone() ||
7214 instr->CanDeoptimize() ||
7215 instr->IsThrow() ||
7216 instr->IsReThrow() ||
7217 instr->IsReturn()) {
7218 // Instructions that return from the function, instructions with side
7219 // effects and instructions that can deoptimize are considered as
7220 // loads from all places.
7221 live_in->CopyFrom(all_places);
7222 if (instr->IsThrow() || instr->IsReThrow() || instr->IsReturn()) {
7223 // Initialize live-out for exit blocks since it won't be computed
7224 // otherwise during the fixed point iteration.
7225 live_out->CopyFrom(all_places);
7226 }
7227 continue;
7228 }
7229
7230 // Handle loads.
7231 Definition* defn = instr->AsDefinition();
7232 if ((defn != NULL) && IsLoadEliminationCandidate(defn)) {
7233 const intptr_t alias = aliased_set_->LookupAliasId(place.ToAlias());
7234 live_in->AddAll(aliased_set_->GetKilledSet(alias));
7235 continue;
7236 }
7237 }
7238 exposed_stores_[postorder_number] = exposed_stores;
7239 }
7240 if (FLAG_trace_load_optimization) {
7241 Dump();
7242 OS::Print("---\n");
7243 }
7244 }
7245
7246 void EliminateDeadStores() {
7247 // Iteration order does not matter here.
7248 for (BlockIterator block_it = graph_->postorder_iterator();
7249 !block_it.Done();
7250 block_it.Advance()) {
7251 BlockEntryInstr* block = block_it.Current();
7252 const intptr_t postorder_number = block->postorder_number();
7253
7254 BitVector* live_out = live_out_[postorder_number];
7255
7256 ZoneGrowableArray<Instruction*>* exposed_stores =
7257 exposed_stores_[postorder_number];
7258 if (exposed_stores == NULL) continue; // No exposed stores.
7259
7260 // Iterate over candidate stores.
7261 for (intptr_t i = 0; i < exposed_stores->length(); ++i) {
7262 Instruction* instr = (*exposed_stores)[i];
7263 bool is_load = false;
7264 bool is_store = false;
7265 Place place(instr, &is_load, &is_store);
7266 ASSERT(!is_load && is_store);
7267 if (place.IsFinalField()) {
7268 // Final field do not participate in dead store elimination.
7269 continue;
7270 }
7271 // Eliminate a downward exposed store if the corresponding place is not
7272 // in live-out.
7273 if (!live_out->Contains(instr->place_id()) &&
7274 CanEliminateStore(instr)) {
7275 if (FLAG_trace_optimization) {
7276 OS::Print("Removing dead store to place %" Pd " block B%" Pd "\n",
7277 instr->place_id(), block->block_id());
7278 }
7279 instr->RemoveFromGraph(/* ignored */ false);
7280 }
7281 }
7282 }
7283 }
7284
7285 FlowGraph* graph_;
7286 DirectChainedHashMap<PointerKeyValueTrait<Place> >* map_;
7287
7288 // Mapping between field offsets in words and expression ids of loads from
7289 // that offset.
7290 AliasedSet* aliased_set_;
7291
7292 // Per block list of downward exposed stores.
7293 GrowableArray<ZoneGrowableArray<Instruction*>*> exposed_stores_;
7294
7295 DISALLOW_COPY_AND_ASSIGN(StoreOptimizer);
7296 };
7297
7298
7299 void DeadStoreElimination::Optimize(FlowGraph* graph) {
7300 if (FLAG_dead_store_elimination) {
7301 StoreOptimizer::OptimizeGraph(graph);
7302 }
7303 }
7304
7305
7306 // Returns true iff this definition is used in a non-phi instruction.
7307 static bool HasRealUse(Definition* def) {
7308 // Environment uses are real (non-phi) uses.
7309 if (def->env_use_list() != NULL) return true;
7310
7311 for (Value::Iterator it(def->input_use_list());
7312 !it.Done();
7313 it.Advance()) {
7314 if (!it.Current()->instruction()->IsPhi()) return true;
7315 }
7316 return false;
7317 }
7318
7319
7320 void DeadCodeElimination::EliminateDeadPhis(FlowGraph* flow_graph) {
7321 GrowableArray<PhiInstr*> live_phis;
7322 for (BlockIterator b = flow_graph->postorder_iterator();
7323 !b.Done();
7324 b.Advance()) {
7325 JoinEntryInstr* join = b.Current()->AsJoinEntry();
7326 if (join != NULL) {
7327 for (PhiIterator it(join); !it.Done(); it.Advance()) {
7328 PhiInstr* phi = it.Current();
7329 // Phis that have uses and phis inside try blocks are
7330 // marked as live.
7331 if (HasRealUse(phi) || join->InsideTryBlock()) {
7332 live_phis.Add(phi);
7333 phi->mark_alive();
7334 } else {
7335 phi->mark_dead();
7336 }
7337 }
7338 }
7339 }
7340
7341 while (!live_phis.is_empty()) {
7342 PhiInstr* phi = live_phis.RemoveLast();
7343 for (intptr_t i = 0; i < phi->InputCount(); i++) {
7344 Value* val = phi->InputAt(i);
7345 PhiInstr* used_phi = val->definition()->AsPhi();
7346 if ((used_phi != NULL) && !used_phi->is_alive()) {
7347 used_phi->mark_alive();
7348 live_phis.Add(used_phi);
7349 }
7350 }
7351 }
7352
7353 for (BlockIterator it(flow_graph->postorder_iterator());
7354 !it.Done();
7355 it.Advance()) {
7356 JoinEntryInstr* join = it.Current()->AsJoinEntry();
7357 if (join != NULL) {
7358 if (join->phis_ == NULL) continue;
7359
7360 // Eliminate dead phis and compact the phis_ array of the block.
7361 intptr_t to_index = 0;
7362 for (intptr_t i = 0; i < join->phis_->length(); ++i) {
7363 PhiInstr* phi = (*join->phis_)[i];
7364 if (phi != NULL) {
7365 if (!phi->is_alive()) {
7366 phi->ReplaceUsesWith(flow_graph->constant_null());
7367 phi->UnuseAllInputs();
7368 (*join->phis_)[i] = NULL;
7369 if (FLAG_trace_optimization) {
7370 OS::Print("Removing dead phi v%" Pd "\n", phi->ssa_temp_index());
7371 }
7372 } else if (phi->IsRedundant()) {
7373 phi->ReplaceUsesWith(phi->InputAt(0)->definition());
7374 phi->UnuseAllInputs();
7375 (*join->phis_)[i] = NULL;
7376 if (FLAG_trace_optimization) {
7377 OS::Print("Removing redundant phi v%" Pd "\n",
7378 phi->ssa_temp_index());
7379 }
7380 } else {
7381 (*join->phis_)[to_index++] = phi;
7382 }
7383 }
7384 }
7385 if (to_index == 0) {
7386 join->phis_ = NULL;
7387 } else {
7388 join->phis_->TruncateTo(to_index);
7389 }
7390 }
7391 }
7392 }
7393
7394
7395 class CSEInstructionMap : public ValueObject {
7396 public:
7397 // Right now CSE and LICM track a single effect: possible externalization of
7398 // strings.
7399 // Other effects like modifications of fields are tracked in a separate load
7400 // forwarding pass via Alias structure.
7401 COMPILE_ASSERT(EffectSet::kLastEffect == 1);
7402
7403 CSEInstructionMap() : independent_(), dependent_() { }
7404 explicit CSEInstructionMap(const CSEInstructionMap& other)
7405 : ValueObject(),
7406 independent_(other.independent_),
7407 dependent_(other.dependent_) {
7408 }
7409
7410 void RemoveAffected(EffectSet effects) {
7411 if (!effects.IsNone()) {
7412 dependent_.Clear();
7413 }
7414 }
7415
7416 Instruction* Lookup(Instruction* other) const {
7417 return GetMapFor(other)->Lookup(other);
7418 }
7419
7420 void Insert(Instruction* instr) {
7421 return GetMapFor(instr)->Insert(instr);
7422 }
7423
7424 private:
7425 typedef DirectChainedHashMap<PointerKeyValueTrait<Instruction> > Map;
7426
7427 Map* GetMapFor(Instruction* instr) {
7428 return instr->Dependencies().IsNone() ? &independent_ : &dependent_;
7429 }
7430
7431 const Map* GetMapFor(Instruction* instr) const {
7432 return instr->Dependencies().IsNone() ? &independent_ : &dependent_;
7433 }
7434
7435 // All computations that are not affected by any side-effect.
7436 // Majority of computations are not affected by anything and will be in
7437 // this map.
7438 Map independent_;
7439
7440 // All computations that are affected by side effect.
7441 Map dependent_;
7442 };
7443
7444
7445 bool DominatorBasedCSE::Optimize(FlowGraph* graph) {
7446 bool changed = false;
7447 if (FLAG_load_cse) {
7448 changed = LoadOptimizer::OptimizeGraph(graph) || changed;
7449 }
7450
7451 CSEInstructionMap map;
7452 changed = OptimizeRecursive(graph, graph->graph_entry(), &map) || changed;
7453
7454 return changed;
7455 }
7456
7457
7458 bool DominatorBasedCSE::OptimizeRecursive(
7459 FlowGraph* graph,
7460 BlockEntryInstr* block,
7461 CSEInstructionMap* map) {
7462 bool changed = false;
7463 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
7464 Instruction* current = it.Current();
7465 if (current->AllowsCSE()) {
7466 Instruction* replacement = map->Lookup(current);
7467 if ((replacement != NULL) &&
7468 graph->block_effects()->IsAvailableAt(replacement, block)) {
7469 // Replace current with lookup result.
7470 ReplaceCurrentInstruction(&it, current, replacement, graph);
7471 changed = true;
7472 continue;
7473 }
7474
7475 // For simplicity we assume that instruction either does not depend on
7476 // anything or does not affect anything. If this is not the case then
7477 // we should first remove affected instructions from the map and
7478 // then add instruction to the map so that it does not kill itself.
7479 ASSERT(current->Effects().IsNone() || current->Dependencies().IsNone());
7480 map->Insert(current);
7481 }
7482
7483 map->RemoveAffected(current->Effects());
7484 }
7485
7486 // Process children in the dominator tree recursively.
7487 intptr_t num_children = block->dominated_blocks().length();
7488 for (intptr_t i = 0; i < num_children; ++i) {
7489 BlockEntryInstr* child = block->dominated_blocks()[i];
7490 if (i < num_children - 1) {
7491 // Copy map.
7492 CSEInstructionMap child_map(*map);
7493 changed = OptimizeRecursive(graph, child, &child_map) || changed;
7494 } else {
7495 // Reuse map for the last child.
7496 changed = OptimizeRecursive(graph, child, map) || changed;
7497 }
7498 }
7499 return changed;
7500 }
7501
7502 25
7503 ConstantPropagator::ConstantPropagator( 26 ConstantPropagator::ConstantPropagator(
7504 FlowGraph* graph, 27 FlowGraph* graph,
7505 const GrowableArray<BlockEntryInstr*>& ignored) 28 const GrowableArray<BlockEntryInstr*>& ignored)
7506 : FlowGraphVisitor(ignored), 29 : FlowGraphVisitor(ignored),
7507 graph_(graph), 30 graph_(graph),
7508 unknown_(Object::unknown_constant()), 31 unknown_(Object::unknown_constant()),
7509 non_constant_(Object::non_constant()), 32 non_constant_(Object::non_constant()),
7510 reachable_(new(graph->isolate()) BitVector( 33 reachable_(new(graph->isolate()) BitVector(
7511 graph->isolate(), graph->preorder().length())), 34 graph->isolate(), graph->preorder().length())),
(...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after
8195 void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) { 718 void ConstantPropagator::VisitInstanceOf(InstanceOfInstr* instr) {
8196 const Definition* def = instr->value()->definition(); 719 const Definition* def = instr->value()->definition();
8197 const Object& value = def->constant_value(); 720 const Object& value = def->constant_value();
8198 if (IsNonConstant(value)) { 721 if (IsNonConstant(value)) {
8199 const AbstractType& checked_type = instr->type(); 722 const AbstractType& checked_type = instr->type();
8200 intptr_t value_cid = instr->value()->Type()->ToCid(); 723 intptr_t value_cid = instr->value()->Type()->ToCid();
8201 Representation rep = def->representation(); 724 Representation rep = def->representation();
8202 if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) || 725 if ((checked_type.IsFloat32x4Type() && (rep == kUnboxedFloat32x4)) ||
8203 (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) || 726 (checked_type.IsInt32x4Type() && (rep == kUnboxedInt32x4)) ||
8204 (checked_type.IsDoubleType() && (rep == kUnboxedDouble) && 727 (checked_type.IsDoubleType() && (rep == kUnboxedDouble) &&
8205 CanUnboxDouble()) || 728 FlowGraphCompiler::SupportsUnboxedDoubles()) ||
8206 (checked_type.IsIntType() && (rep == kUnboxedMint))) { 729 (checked_type.IsIntType() && (rep == kUnboxedMint))) {
8207 // Ensure that compile time type matches representation. 730 // Ensure that compile time type matches representation.
8208 ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) || 731 ASSERT(((rep == kUnboxedFloat32x4) && (value_cid == kFloat32x4Cid)) ||
8209 ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) || 732 ((rep == kUnboxedInt32x4) && (value_cid == kInt32x4Cid)) ||
8210 ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) || 733 ((rep == kUnboxedDouble) && (value_cid == kDoubleCid)) ||
8211 ((rep == kUnboxedMint) && (value_cid == kMintCid))); 734 ((rep == kUnboxedMint) && (value_cid == kMintCid)));
8212 // The representation guarantees the type check to be true. 735 // The representation guarantees the type check to be true.
8213 SetValue(instr, instr->negate_result() ? Bool::False() : Bool::True()); 736 SetValue(instr, instr->negate_result() ? Bool::False() : Bool::True());
8214 } else { 737 } else {
8215 SetValue(instr, non_constant_); 738 SetValue(instr, non_constant_);
(...skipping 889 matching lines...) Expand 10 before | Expand all | Expand 10 after
9105 GrowableArray<BitVector*> dominance_frontier; 1628 GrowableArray<BitVector*> dominance_frontier;
9106 graph_->ComputeDominators(&dominance_frontier); 1629 graph_->ComputeDominators(&dominance_frontier);
9107 1630
9108 if (FLAG_trace_constant_propagation) { 1631 if (FLAG_trace_constant_propagation) {
9109 OS::Print("\n==== After constant propagation ====\n"); 1632 OS::Print("\n==== After constant propagation ====\n");
9110 FlowGraphPrinter printer(*graph_); 1633 FlowGraphPrinter printer(*graph_);
9111 printer.PrintBlocks(); 1634 printer.PrintBlocks();
9112 } 1635 }
9113 } 1636 }
9114 1637
9115
9116 // Returns true if the given phi has a single input use and
9117 // is used in the environments either at the corresponding block entry or
9118 // at the same instruction where input use is.
9119 static bool PhiHasSingleUse(PhiInstr* phi, Value* use) {
9120 if ((use->next_use() != NULL) || (phi->input_use_list() != use)) {
9121 return false;
9122 }
9123
9124 BlockEntryInstr* block = phi->block();
9125 for (Value* env_use = phi->env_use_list();
9126 env_use != NULL;
9127 env_use = env_use->next_use()) {
9128 if ((env_use->instruction() != block) &&
9129 (env_use->instruction() != use->instruction())) {
9130 return false;
9131 }
9132 }
9133
9134 return true;
9135 }
9136
9137
9138 bool BranchSimplifier::Match(JoinEntryInstr* block) {
9139 // Match the pattern of a branch on a comparison whose left operand is a
9140 // phi from the same block, and whose right operand is a constant.
9141 //
9142 // Branch(Comparison(kind, Phi, Constant))
9143 //
9144 // These are the branches produced by inlining in a test context. Also,
9145 // the phi has no other uses so they can simply be eliminated. The block
9146 // has no other phis and no instructions intervening between the phi and
9147 // branch so the block can simply be eliminated.
9148 BranchInstr* branch = block->last_instruction()->AsBranch();
9149 ASSERT(branch != NULL);
9150 ComparisonInstr* comparison = branch->comparison();
9151 Value* left = comparison->left();
9152 PhiInstr* phi = left->definition()->AsPhi();
9153 Value* right = comparison->right();
9154 ConstantInstr* constant =
9155 (right == NULL) ? NULL : right->definition()->AsConstant();
9156 return (phi != NULL) &&
9157 (constant != NULL) &&
9158 (phi->GetBlock() == block) &&
9159 PhiHasSingleUse(phi, left) &&
9160 (block->next() == branch) &&
9161 (block->phis()->length() == 1);
9162 }
9163
9164
9165 JoinEntryInstr* BranchSimplifier::ToJoinEntry(Isolate* isolate,
9166 TargetEntryInstr* target) {
9167 // Convert a target block into a join block. Branches will be duplicated
9168 // so the former true and false targets become joins of the control flows
9169 // from all the duplicated branches.
9170 JoinEntryInstr* join =
9171 new(isolate) JoinEntryInstr(target->block_id(), target->try_index());
9172 join->InheritDeoptTarget(isolate, target);
9173 join->LinkTo(target->next());
9174 join->set_last_instruction(target->last_instruction());
9175 target->UnuseAllInputs();
9176 return join;
9177 }
9178
9179
9180 BranchInstr* BranchSimplifier::CloneBranch(Isolate* isolate,
9181 BranchInstr* branch,
9182 Value* new_left,
9183 Value* new_right) {
9184 ComparisonInstr* comparison = branch->comparison();
9185 ComparisonInstr* new_comparison =
9186 comparison->CopyWithNewOperands(new_left, new_right);
9187 BranchInstr* new_branch = new(isolate) BranchInstr(new_comparison);
9188 new_branch->set_is_checked(branch->is_checked());
9189 return new_branch;
9190 }
9191
9192
9193 void BranchSimplifier::Simplify(FlowGraph* flow_graph) {
9194 // Optimize some branches that test the value of a phi. When it is safe
9195 // to do so, push the branch to each of the predecessor blocks. This is
9196 // an optimization when (a) it can avoid materializing a boolean object at
9197 // the phi only to test its value, and (b) it can expose opportunities for
9198 // constant propagation and unreachable code elimination. This
9199 // optimization is intended to run after inlining which creates
9200 // opportunities for optimization (a) and before constant folding which
9201 // can perform optimization (b).
9202
9203 // Begin with a worklist of join blocks ending in branches. They are
9204 // candidates for the pattern below.
9205 Isolate* isolate = flow_graph->isolate();
9206 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
9207 GrowableArray<BlockEntryInstr*> worklist(postorder.length());
9208 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
9209 BlockEntryInstr* block = it.Current();
9210 if (block->IsJoinEntry() && block->last_instruction()->IsBranch()) {
9211 worklist.Add(block);
9212 }
9213 }
9214
9215 // Rewrite until no more instance of the pattern exists.
9216 bool changed = false;
9217 while (!worklist.is_empty()) {
9218 // All blocks in the worklist are join blocks (ending with a branch).
9219 JoinEntryInstr* block = worklist.RemoveLast()->AsJoinEntry();
9220 ASSERT(block != NULL);
9221
9222 if (Match(block)) {
9223 changed = true;
9224
9225 // The branch will be copied and pushed to all the join's
9226 // predecessors. Convert the true and false target blocks into join
9227 // blocks to join the control flows from all of the true
9228 // (respectively, false) targets of the copied branches.
9229 //
9230 // The converted join block will have no phis, so it cannot be another
9231 // instance of the pattern. There is thus no need to add it to the
9232 // worklist.
9233 BranchInstr* branch = block->last_instruction()->AsBranch();
9234 ASSERT(branch != NULL);
9235 JoinEntryInstr* join_true =
9236 ToJoinEntry(isolate, branch->true_successor());
9237 JoinEntryInstr* join_false =
9238 ToJoinEntry(isolate, branch->false_successor());
9239
9240 ComparisonInstr* comparison = branch->comparison();
9241 PhiInstr* phi = comparison->left()->definition()->AsPhi();
9242 ConstantInstr* constant = comparison->right()->definition()->AsConstant();
9243 ASSERT(constant != NULL);
9244 // Copy the constant and branch and push it to all the predecessors.
9245 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) {
9246 GotoInstr* old_goto =
9247 block->PredecessorAt(i)->last_instruction()->AsGoto();
9248 ASSERT(old_goto != NULL);
9249
9250 // Replace the goto in each predecessor with a rewritten branch,
9251 // rewritten to use the corresponding phi input instead of the phi.
9252 Value* new_left = phi->InputAt(i)->Copy(isolate);
9253 Value* new_right = new(isolate) Value(constant);
9254 BranchInstr* new_branch =
9255 CloneBranch(isolate, branch, new_left, new_right);
9256 if (branch->env() == NULL) {
9257 new_branch->InheritDeoptTarget(isolate, old_goto);
9258 } else {
9259 // Take the environment from the branch if it has one.
9260 new_branch->InheritDeoptTarget(isolate, branch);
9261 // InheritDeoptTarget gave the new branch's comparison the same
9262 // deopt id that it gave the new branch. The id should be the
9263 // deopt id of the original comparison.
9264 new_branch->comparison()->SetDeoptId(*comparison);
9265 // The phi can be used in the branch's environment. Rename such
9266 // uses.
9267 for (Environment::DeepIterator it(new_branch->env());
9268 !it.Done();
9269 it.Advance()) {
9270 Value* use = it.CurrentValue();
9271 if (use->definition() == phi) {
9272 Definition* replacement = phi->InputAt(i)->definition();
9273 use->RemoveFromUseList();
9274 use->set_definition(replacement);
9275 replacement->AddEnvUse(use);
9276 }
9277 }
9278 }
9279
9280 new_branch->InsertBefore(old_goto);
9281 new_branch->set_next(NULL); // Detaching the goto from the graph.
9282 old_goto->UnuseAllInputs();
9283
9284 // Update the predecessor block. We may have created another
9285 // instance of the pattern so add it to the worklist if necessary.
9286 BlockEntryInstr* branch_block = new_branch->GetBlock();
9287 branch_block->set_last_instruction(new_branch);
9288 if (branch_block->IsJoinEntry()) worklist.Add(branch_block);
9289
9290 // Connect the branch to the true and false joins, via empty target
9291 // blocks.
9292 TargetEntryInstr* true_target =
9293 new(isolate) TargetEntryInstr(flow_graph->max_block_id() + 1,
9294 block->try_index());
9295 true_target->InheritDeoptTarget(isolate, join_true);
9296 TargetEntryInstr* false_target =
9297 new(isolate) TargetEntryInstr(flow_graph->max_block_id() + 2,
9298 block->try_index());
9299 false_target->InheritDeoptTarget(isolate, join_false);
9300 flow_graph->set_max_block_id(flow_graph->max_block_id() + 2);
9301 *new_branch->true_successor_address() = true_target;
9302 *new_branch->false_successor_address() = false_target;
9303 GotoInstr* goto_true = new(isolate) GotoInstr(join_true);
9304 goto_true->InheritDeoptTarget(isolate, join_true);
9305 true_target->LinkTo(goto_true);
9306 true_target->set_last_instruction(goto_true);
9307 GotoInstr* goto_false = new(isolate) GotoInstr(join_false);
9308 goto_false->InheritDeoptTarget(isolate, join_false);
9309 false_target->LinkTo(goto_false);
9310 false_target->set_last_instruction(goto_false);
9311 }
9312 // When all predecessors have been rewritten, the original block is
9313 // unreachable from the graph.
9314 phi->UnuseAllInputs();
9315 branch->UnuseAllInputs();
9316 block->UnuseAllInputs();
9317 ASSERT(!phi->HasUses());
9318 }
9319 }
9320
9321 if (changed) {
9322 // We may have changed the block order and the dominator tree.
9323 flow_graph->DiscoverBlocks();
9324 GrowableArray<BitVector*> dominance_frontier;
9325 flow_graph->ComputeDominators(&dominance_frontier);
9326 }
9327 }
9328
9329
9330 static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) {
9331 return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) &&
9332 ((block->next() == block->last_instruction()) ||
9333 ((block->next() == defn) && (defn->next() == block->last_instruction())));
9334 }
9335
9336
9337 static void EliminateTrivialBlock(BlockEntryInstr* block,
9338 Definition* instr,
9339 IfThenElseInstr* before) {
9340 block->UnuseAllInputs();
9341 block->last_instruction()->UnuseAllInputs();
9342
9343 if ((block->next() == instr) &&
9344 (instr->next() == block->last_instruction())) {
9345 before->previous()->LinkTo(instr);
9346 instr->LinkTo(before);
9347 }
9348 }
9349
9350
9351 void IfConverter::Simplify(FlowGraph* flow_graph) {
9352 Isolate* isolate = flow_graph->isolate();
9353 bool changed = false;
9354
9355 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
9356 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
9357 BlockEntryInstr* block = it.Current();
9358 JoinEntryInstr* join = block->AsJoinEntry();
9359
9360 // Detect diamond control flow pattern which materializes a value depending
9361 // on the result of the comparison:
9362 //
9363 // B_pred:
9364 // ...
9365 // Branch if COMP goto (B_pred1, B_pred2)
9366 // B_pred1: -- trivial block that contains at most one definition
9367 // v1 = Constant(...)
9368 // goto B_block
9369 // B_pred2: -- trivial block that contains at most one definition
9370 // v2 = Constant(...)
9371 // goto B_block
9372 // B_block:
9373 // v3 = phi(v1, v2) -- single phi
9374 //
9375 // and replace it with
9376 //
9377 // Ba:
9378 // v3 = IfThenElse(COMP ? v1 : v2)
9379 //
9380 if ((join != NULL) &&
9381 (join->phis() != NULL) &&
9382 (join->phis()->length() == 1) &&
9383 (block->PredecessorCount() == 2)) {
9384 BlockEntryInstr* pred1 = block->PredecessorAt(0);
9385 BlockEntryInstr* pred2 = block->PredecessorAt(1);
9386
9387 PhiInstr* phi = (*join->phis())[0];
9388 Value* v1 = phi->InputAt(0);
9389 Value* v2 = phi->InputAt(1);
9390
9391 if (IsTrivialBlock(pred1, v1->definition()) &&
9392 IsTrivialBlock(pred2, v2->definition()) &&
9393 (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) {
9394 BlockEntryInstr* pred = pred1->PredecessorAt(0);
9395 BranchInstr* branch = pred->last_instruction()->AsBranch();
9396 ComparisonInstr* comparison = branch->comparison();
9397
9398 // Check if the platform supports efficient branchless IfThenElseInstr
9399 // for the given combination of comparison and values flowing from
9400 // false and true paths.
9401 if (IfThenElseInstr::Supports(comparison, v1, v2)) {
9402 Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2;
9403 Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2;
9404
9405 ComparisonInstr* new_comparison =
9406 comparison->CopyWithNewOperands(
9407 comparison->left()->Copy(isolate),
9408 comparison->right()->Copy(isolate));
9409 IfThenElseInstr* if_then_else = new(isolate) IfThenElseInstr(
9410 new_comparison,
9411 if_true->Copy(isolate),
9412 if_false->Copy(isolate));
9413 flow_graph->InsertBefore(branch,
9414 if_then_else,
9415 NULL,
9416 FlowGraph::kValue);
9417
9418 phi->ReplaceUsesWith(if_then_else);
9419
9420 // Connect IfThenElseInstr to the first instruction in the merge block
9421 // effectively eliminating diamond control flow.
9422 // Current block as well as pred1 and pred2 blocks are no longer in
9423 // the graph at this point.
9424 if_then_else->LinkTo(join->next());
9425 pred->set_last_instruction(join->last_instruction());
9426
9427 // Resulting block must inherit block id from the eliminated current
9428 // block to guarantee that ordering of phi operands in its successor
9429 // stays consistent.
9430 pred->set_block_id(block->block_id());
9431
9432 // If v1 and v2 were defined inside eliminated blocks pred1/pred2
9433 // move them out to the place before inserted IfThenElse instruction.
9434 EliminateTrivialBlock(pred1, v1->definition(), if_then_else);
9435 EliminateTrivialBlock(pred2, v2->definition(), if_then_else);
9436
9437 // Update use lists to reflect changes in the graph.
9438 phi->UnuseAllInputs();
9439 branch->UnuseAllInputs();
9440 block->UnuseAllInputs();
9441
9442 // The graph has changed. Recompute dominators and block orders after
9443 // this pass is finished.
9444 changed = true;
9445 }
9446 }
9447 }
9448 }
9449
9450 if (changed) {
9451 // We may have changed the block order and the dominator tree.
9452 flow_graph->DiscoverBlocks();
9453 GrowableArray<BitVector*> dominance_frontier;
9454 flow_graph->ComputeDominators(&dominance_frontier);
9455 }
9456 }
9457
9458
9459 void FlowGraphOptimizer::EliminateEnvironments() {
9460 // After this pass we can no longer perform LICM and hoist instructions
9461 // that can deoptimize.
9462
9463 flow_graph_->disallow_licm();
9464 for (intptr_t i = 0; i < block_order_.length(); ++i) {
9465 BlockEntryInstr* block = block_order_[i];
9466 block->RemoveEnvironment();
9467 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
9468 Instruction* current = it.Current();
9469 if (!current->CanDeoptimize()) {
9470 // TODO(srdjan): --source-lines needs deopt environments to get at
9471 // the code for this instruction, however, leaving the environment
9472 // changes code.
9473 current->RemoveEnvironment();
9474 }
9475 }
9476 }
9477 }
9478
9479
9480 enum SafeUseCheck { kOptimisticCheck, kStrictCheck };
9481
9482 // Check if the use is safe for allocation sinking. Allocation sinking
9483 // candidates can only be used at store instructions:
9484 //
9485 // - any store into the allocation candidate itself is unconditionally safe
9486 // as it just changes the rematerialization state of this candidate;
9487 // - store into another object is only safe if another object is allocation
9488 // candidate.
9489 //
9490 // We use a simple fix-point algorithm to discover the set of valid candidates
9491 // (see CollectCandidates method), that's why this IsSafeUse can operate in two
9492 // modes:
9493 //
9494 // - optimistic, when every allocation is assumed to be an allocation
9495 // sinking candidate;
9496 // - strict, when only marked allocations are assumed to be allocation
9497 // sinking candidates.
9498 //
9499 // Fix-point algorithm in CollectCandiates first collects a set of allocations
9500 // optimistically and then checks each collected candidate strictly and unmarks
9501 // invalid candidates transitively until only strictly valid ones remain.
9502 static bool IsSafeUse(Value* use, SafeUseCheck check_type) {
9503 if (use->instruction()->IsMaterializeObject()) {
9504 return true;
9505 }
9506
9507 StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField();
9508 if (store != NULL) {
9509 if (use == store->value()) {
9510 Definition* instance = store->instance()->definition();
9511 return instance->IsAllocateObject() &&
9512 ((check_type == kOptimisticCheck) ||
9513 instance->Identity().IsAllocationSinkingCandidate());
9514 }
9515 return true;
9516 }
9517
9518 return false;
9519 }
9520
9521
9522 // Right now we are attempting to sink allocation only into
9523 // deoptimization exit. So candidate should only be used in StoreInstanceField
9524 // instructions that write into fields of the allocated object.
9525 // We do not support materialization of the object that has type arguments.
9526 static bool IsAllocationSinkingCandidate(Definition* alloc,
9527 SafeUseCheck check_type) {
9528 for (Value* use = alloc->input_use_list();
9529 use != NULL;
9530 use = use->next_use()) {
9531 if (!IsSafeUse(use, check_type)) {
9532 if (FLAG_trace_optimization) {
9533 OS::Print("use of %s at %s is unsafe for allocation sinking\n",
9534 alloc->ToCString(),
9535 use->instruction()->ToCString());
9536 }
9537 return false;
9538 }
9539 }
9540
9541 return true;
9542 }
9543
9544
9545 // If the given use is a store into an object then return an object we are
9546 // storing into.
9547 static Definition* StoreInto(Value* use) {
9548 StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField();
9549 if (store != NULL) {
9550 return store->instance()->definition();
9551 }
9552
9553 return NULL;
9554 }
9555
9556
9557 // Remove the given allocation from the graph. It is not observable.
9558 // If deoptimization occurs the object will be materialized.
9559 void AllocationSinking::EliminateAllocation(Definition* alloc) {
9560 ASSERT(IsAllocationSinkingCandidate(alloc, kStrictCheck));
9561
9562 if (FLAG_trace_optimization) {
9563 OS::Print("removing allocation from the graph: v%" Pd "\n",
9564 alloc->ssa_temp_index());
9565 }
9566
9567 // As an allocation sinking candidate it is only used in stores to its own
9568 // fields. Remove these stores.
9569 for (Value* use = alloc->input_use_list();
9570 use != NULL;
9571 use = alloc->input_use_list()) {
9572 use->instruction()->RemoveFromGraph();
9573 }
9574
9575 // There should be no environment uses. The pass replaced them with
9576 // MaterializeObject instructions.
9577 #ifdef DEBUG
9578 for (Value* use = alloc->env_use_list();
9579 use != NULL;
9580 use = use->next_use()) {
9581 ASSERT(use->instruction()->IsMaterializeObject());
9582 }
9583 #endif
9584 ASSERT(alloc->input_use_list() == NULL);
9585 alloc->RemoveFromGraph();
9586 if (alloc->ArgumentCount() > 0) {
9587 ASSERT(alloc->ArgumentCount() == 1);
9588 for (intptr_t i = 0; i < alloc->ArgumentCount(); ++i) {
9589 alloc->PushArgumentAt(i)->RemoveFromGraph();
9590 }
9591 }
9592 }
9593
9594
9595 // Find allocation instructions that can be potentially eliminated and
9596 // rematerialized at deoptimization exits if needed. See IsSafeUse
9597 // for the description of algorithm used below.
9598 void AllocationSinking::CollectCandidates() {
9599 // Optimistically collect all potential candidates.
9600 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
9601 !block_it.Done();
9602 block_it.Advance()) {
9603 BlockEntryInstr* block = block_it.Current();
9604 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
9605 { AllocateObjectInstr* alloc = it.Current()->AsAllocateObject();
9606 if ((alloc != NULL) &&
9607 IsAllocationSinkingCandidate(alloc, kOptimisticCheck)) {
9608 alloc->SetIdentity(AliasIdentity::AllocationSinkingCandidate());
9609 candidates_.Add(alloc);
9610 }
9611 }
9612 { AllocateUninitializedContextInstr* alloc =
9613 it.Current()->AsAllocateUninitializedContext();
9614 if ((alloc != NULL) &&
9615 IsAllocationSinkingCandidate(alloc, kOptimisticCheck)) {
9616 alloc->SetIdentity(AliasIdentity::AllocationSinkingCandidate());
9617 candidates_.Add(alloc);
9618 }
9619 }
9620 }
9621 }
9622
9623 // Transitively unmark all candidates that are not strictly valid.
9624 bool changed;
9625 do {
9626 changed = false;
9627 for (intptr_t i = 0; i < candidates_.length(); i++) {
9628 Definition* alloc = candidates_[i];
9629 if (alloc->Identity().IsAllocationSinkingCandidate()) {
9630 if (!IsAllocationSinkingCandidate(alloc, kStrictCheck)) {
9631 alloc->SetIdentity(AliasIdentity::Unknown());
9632 changed = true;
9633 }
9634 }
9635 }
9636 } while (changed);
9637
9638 // Shrink the list of candidates removing all unmarked ones.
9639 intptr_t j = 0;
9640 for (intptr_t i = 0; i < candidates_.length(); i++) {
9641 Definition* alloc = candidates_[i];
9642 if (alloc->Identity().IsAllocationSinkingCandidate()) {
9643 if (FLAG_trace_optimization) {
9644 OS::Print("discovered allocation sinking candidate: v%" Pd "\n",
9645 alloc->ssa_temp_index());
9646 }
9647
9648 if (j != i) {
9649 candidates_[j] = alloc;
9650 }
9651 j++;
9652 }
9653 }
9654 candidates_.TruncateTo(j);
9655 }
9656
9657
9658 // If materialization references an allocation sinking candidate then replace
9659 // this reference with a materialization which should have been computed for
9660 // this side-exit. CollectAllExits should have collected this exit.
9661 void AllocationSinking::NormalizeMaterializations() {
9662 for (intptr_t i = 0; i < candidates_.length(); i++) {
9663 Definition* alloc = candidates_[i];
9664
9665 Value* next_use;
9666 for (Value* use = alloc->input_use_list();
9667 use != NULL;
9668 use = next_use) {
9669 next_use = use->next_use();
9670 if (use->instruction()->IsMaterializeObject()) {
9671 use->BindTo(MaterializationFor(alloc, use->instruction()));
9672 }
9673 }
9674 }
9675 }
9676
9677
9678 // We transitively insert materializations at each deoptimization exit that
9679 // might see the given allocation (see ExitsCollector). Some of this
9680 // materializations are not actually used and some fail to compute because
9681 // they are inserted in the block that is not dominated by the allocation.
9682 // Remove them unused materializations from the graph.
9683 void AllocationSinking::RemoveUnusedMaterializations() {
9684 intptr_t j = 0;
9685 for (intptr_t i = 0; i < materializations_.length(); i++) {
9686 MaterializeObjectInstr* mat = materializations_[i];
9687 if ((mat->input_use_list() == NULL) && (mat->env_use_list() == NULL)) {
9688 // Check if this materialization failed to compute and remove any
9689 // unforwarded loads. There were no loads from any allocation sinking
9690 // candidate in the beggining so it is safe to assume that any encountered
9691 // load was inserted by CreateMaterializationAt.
9692 for (intptr_t i = 0; i < mat->InputCount(); i++) {
9693 LoadFieldInstr* load = mat->InputAt(i)->definition()->AsLoadField();
9694 if ((load != NULL) &&
9695 (load->instance()->definition() == mat->allocation())) {
9696 load->ReplaceUsesWith(flow_graph_->constant_null());
9697 load->RemoveFromGraph();
9698 }
9699 }
9700 mat->RemoveFromGraph();
9701 } else {
9702 if (j != i) {
9703 materializations_[j] = mat;
9704 }
9705 j++;
9706 }
9707 }
9708 materializations_.TruncateTo(j);
9709 }
9710
9711
9712 // Some candidates might stop being eligible for allocation sinking after
9713 // the load forwarding because they flow into phis that load forwarding
9714 // inserts. Discover such allocations and remove them from the list
9715 // of allocation sinking candidates undoing all changes that we did
9716 // in preparation for sinking these allocations.
9717 void AllocationSinking::DiscoverFailedCandidates() {
9718 // Transitively unmark all candidates that are not strictly valid.
9719 bool changed;
9720 do {
9721 changed = false;
9722 for (intptr_t i = 0; i < candidates_.length(); i++) {
9723 Definition* alloc = candidates_[i];
9724 if (alloc->Identity().IsAllocationSinkingCandidate()) {
9725 if (!IsAllocationSinkingCandidate(alloc, kStrictCheck)) {
9726 alloc->SetIdentity(AliasIdentity::Unknown());
9727 changed = true;
9728 }
9729 }
9730 }
9731 } while (changed);
9732
9733 // Remove all failed candidates from the candidates list.
9734 intptr_t j = 0;
9735 for (intptr_t i = 0; i < candidates_.length(); i++) {
9736 Definition* alloc = candidates_[i];
9737 if (!alloc->Identity().IsAllocationSinkingCandidate()) {
9738 if (FLAG_trace_optimization) {
9739 OS::Print("allocation v%" Pd " can't be eliminated\n",
9740 alloc->ssa_temp_index());
9741 }
9742
9743 #ifdef DEBUG
9744 for (Value* use = alloc->env_use_list();
9745 use != NULL;
9746 use = use->next_use()) {
9747 ASSERT(use->instruction()->IsMaterializeObject());
9748 }
9749 #endif
9750
9751 // All materializations will be removed from the graph. Remove inserted
9752 // loads first and detach materializations from allocation's environment
9753 // use list: we will reconstruct it when we start removing
9754 // materializations.
9755 alloc->set_env_use_list(NULL);
9756 for (Value* use = alloc->input_use_list();
9757 use != NULL;
9758 use = use->next_use()) {
9759 if (use->instruction()->IsLoadField()) {
9760 LoadFieldInstr* load = use->instruction()->AsLoadField();
9761 load->ReplaceUsesWith(flow_graph_->constant_null());
9762 load->RemoveFromGraph();
9763 } else {
9764 ASSERT(use->instruction()->IsMaterializeObject() ||
9765 use->instruction()->IsPhi() ||
9766 use->instruction()->IsStoreInstanceField());
9767 }
9768 }
9769 } else {
9770 if (j != i) {
9771 candidates_[j] = alloc;
9772 }
9773 j++;
9774 }
9775 }
9776
9777 if (j != candidates_.length()) { // Something was removed from candidates.
9778 intptr_t k = 0;
9779 for (intptr_t i = 0; i < materializations_.length(); i++) {
9780 MaterializeObjectInstr* mat = materializations_[i];
9781 if (!mat->allocation()->Identity().IsAllocationSinkingCandidate()) {
9782 // Restore environment uses of the allocation that were replaced
9783 // by this materialization and drop materialization.
9784 mat->ReplaceUsesWith(mat->allocation());
9785 mat->RemoveFromGraph();
9786 } else {
9787 if (k != i) {
9788 materializations_[k] = mat;
9789 }
9790 k++;
9791 }
9792 }
9793 materializations_.TruncateTo(k);
9794 }
9795
9796 candidates_.TruncateTo(j);
9797 }
9798
9799
9800 void AllocationSinking::Optimize() {
9801 CollectCandidates();
9802
9803 // Insert MaterializeObject instructions that will describe the state of the
9804 // object at all deoptimization points. Each inserted materialization looks
9805 // like this (where v_0 is allocation that we are going to eliminate):
9806 // v_1 <- LoadField(v_0, field_1)
9807 // ...
9808 // v_N <- LoadField(v_0, field_N)
9809 // v_{N+1} <- MaterializeObject(field_1 = v_1, ..., field_N = v_{N})
9810 for (intptr_t i = 0; i < candidates_.length(); i++) {
9811 InsertMaterializations(candidates_[i]);
9812 }
9813
9814 // Run load forwarding to eliminate LoadField instructions inserted above.
9815 // All loads will be successfully eliminated because:
9816 // a) they use fields (not offsets) and thus provide precise aliasing
9817 // information
9818 // b) candidate does not escape and thus its fields is not affected by
9819 // external effects from calls.
9820 LoadOptimizer::OptimizeGraph(flow_graph_);
9821
9822 NormalizeMaterializations();
9823
9824 RemoveUnusedMaterializations();
9825
9826 // If any candidates are no longer eligible for allocation sinking abort
9827 // the optimization for them and undo any changes we did in preparation.
9828 DiscoverFailedCandidates();
9829
9830 // At this point we have computed the state of object at each deoptimization
9831 // point and we can eliminate it. Loads inserted above were forwarded so there
9832 // are no uses of the allocation just as in the begging of the pass.
9833 for (intptr_t i = 0; i < candidates_.length(); i++) {
9834 EliminateAllocation(candidates_[i]);
9835 }
9836
9837 // Process materializations and unbox their arguments: materializations
9838 // are part of the environment and can materialize boxes for double/mint/simd
9839 // values when needed.
9840 // TODO(vegorov): handle all box types here.
9841 for (intptr_t i = 0; i < materializations_.length(); i++) {
9842 MaterializeObjectInstr* mat = materializations_[i];
9843 for (intptr_t j = 0; j < mat->InputCount(); j++) {
9844 Definition* defn = mat->InputAt(j)->definition();
9845 if (defn->IsBox()) {
9846 mat->InputAt(j)->BindTo(defn->InputAt(0)->definition());
9847 }
9848 }
9849 }
9850 }
9851
9852
9853 // Remove materializations from the graph. Register allocator will treat them
9854 // as part of the environment not as a real instruction.
9855 void AllocationSinking::DetachMaterializations() {
9856 for (intptr_t i = 0; i < materializations_.length(); i++) {
9857 materializations_[i]->previous()->LinkTo(materializations_[i]->next());
9858 }
9859 }
9860
9861
9862 // Add a field/offset to the list of fields if it is not yet present there.
9863 static bool AddSlot(ZoneGrowableArray<const Object*>* slots,
9864 const Object& slot) {
9865 for (intptr_t i = 0; i < slots->length(); i++) {
9866 if ((*slots)[i]->raw() == slot.raw()) {
9867 return false;
9868 }
9869 }
9870 slots->Add(&slot);
9871 return true;
9872 }
9873
9874
9875 // Find deoptimization exit for the given materialization assuming that all
9876 // materializations are emitted right before the instruction which is a
9877 // deoptimization exit.
9878 static Instruction* ExitForMaterialization(MaterializeObjectInstr* mat) {
9879 while (mat->next()->IsMaterializeObject()) {
9880 mat = mat->next()->AsMaterializeObject();
9881 }
9882 return mat->next();
9883 }
9884
9885
9886 // Given the deoptimization exit find first materialization that was inserted
9887 // before it.
9888 static Instruction* FirstMaterializationAt(Instruction* exit) {
9889 while (exit->previous()->IsMaterializeObject()) {
9890 exit = exit->previous();
9891 }
9892 return exit;
9893 }
9894
9895
9896 // Given the allocation and deoptimization exit try to find MaterializeObject
9897 // instruction corresponding to this allocation at this exit.
9898 MaterializeObjectInstr* AllocationSinking::MaterializationFor(
9899 Definition* alloc, Instruction* exit) {
9900 if (exit->IsMaterializeObject()) {
9901 exit = ExitForMaterialization(exit->AsMaterializeObject());
9902 }
9903
9904 for (MaterializeObjectInstr* mat = exit->previous()->AsMaterializeObject();
9905 mat != NULL;
9906 mat = mat->previous()->AsMaterializeObject()) {
9907 if (mat->allocation() == alloc) {
9908 return mat;
9909 }
9910 }
9911
9912 return NULL;
9913 }
9914
9915
9916 // Insert MaterializeObject instruction for the given allocation before
9917 // the given instruction that can deoptimize.
9918 void AllocationSinking::CreateMaterializationAt(
9919 Instruction* exit,
9920 Definition* alloc,
9921 const ZoneGrowableArray<const Object*>& slots) {
9922 ZoneGrowableArray<Value*>* values =
9923 new(I) ZoneGrowableArray<Value*>(slots.length());
9924
9925 // All loads should be inserted before the first materialization so that
9926 // IR follows the following pattern: loads, materializations, deoptimizing
9927 // instruction.
9928 Instruction* load_point = FirstMaterializationAt(exit);
9929
9930 // Insert load instruction for every field.
9931 for (intptr_t i = 0; i < slots.length(); i++) {
9932 LoadFieldInstr* load = slots[i]->IsField()
9933 ? new(I) LoadFieldInstr(
9934 new(I) Value(alloc),
9935 &Field::Cast(*slots[i]),
9936 AbstractType::ZoneHandle(I),
9937 alloc->token_pos())
9938 : new(I) LoadFieldInstr(
9939 new(I) Value(alloc),
9940 Smi::Cast(*slots[i]).Value(),
9941 AbstractType::ZoneHandle(I),
9942 alloc->token_pos());
9943 flow_graph_->InsertBefore(
9944 load_point, load, NULL, FlowGraph::kValue);
9945 values->Add(new(I) Value(load));
9946 }
9947
9948 MaterializeObjectInstr* mat = NULL;
9949 if (alloc->IsAllocateObject()) {
9950 mat = new(I) MaterializeObjectInstr(
9951 alloc->AsAllocateObject(), slots, values);
9952 } else {
9953 ASSERT(alloc->IsAllocateUninitializedContext());
9954 mat = new(I) MaterializeObjectInstr(
9955 alloc->AsAllocateUninitializedContext(), slots, values);
9956 }
9957
9958 flow_graph_->InsertBefore(exit, mat, NULL, FlowGraph::kValue);
9959
9960 // Replace all mentions of this allocation with a newly inserted
9961 // MaterializeObject instruction.
9962 // We must preserve the identity: all mentions are replaced by the same
9963 // materialization.
9964 for (Environment::DeepIterator env_it(exit->env());
9965 !env_it.Done();
9966 env_it.Advance()) {
9967 Value* use = env_it.CurrentValue();
9968 if (use->definition() == alloc) {
9969 use->RemoveFromUseList();
9970 use->set_definition(mat);
9971 mat->AddEnvUse(use);
9972 }
9973 }
9974
9975 // Mark MaterializeObject as an environment use of this allocation.
9976 // This will allow us to discover it when we are looking for deoptimization
9977 // exits for another allocation that potentially flows into this one.
9978 Value* val = new(I) Value(alloc);
9979 val->set_instruction(mat);
9980 alloc->AddEnvUse(val);
9981
9982 // Record inserted materialization.
9983 materializations_.Add(mat);
9984 }
9985
9986
9987 // Add given instruction to the list of the instructions if it is not yet
9988 // present there.
9989 template<typename T>
9990 void AddInstruction(GrowableArray<T*>* list, T* value) {
9991 ASSERT(!value->IsGraphEntry());
9992 for (intptr_t i = 0; i < list->length(); i++) {
9993 if ((*list)[i] == value) {
9994 return;
9995 }
9996 }
9997 list->Add(value);
9998 }
9999
10000
10001 // Transitively collect all deoptimization exits that might need this allocation
10002 // rematerialized. It is not enough to collect only environment uses of this
10003 // allocation because it can flow into other objects that will be
10004 // dematerialized and that are referenced by deopt environments that
10005 // don't contain this allocation explicitly.
10006 void AllocationSinking::ExitsCollector::Collect(Definition* alloc) {
10007 for (Value* use = alloc->env_use_list();
10008 use != NULL;
10009 use = use->next_use()) {
10010 if (use->instruction()->IsMaterializeObject()) {
10011 AddInstruction(&exits_, ExitForMaterialization(
10012 use->instruction()->AsMaterializeObject()));
10013 } else {
10014 AddInstruction(&exits_, use->instruction());
10015 }
10016 }
10017
10018 // Check if this allocation is stored into any other allocation sinking
10019 // candidate and put it on worklist so that we conservatively collect all
10020 // exits for that candidate as well because they potentially might see
10021 // this object.
10022 for (Value* use = alloc->input_use_list();
10023 use != NULL;
10024 use = use->next_use()) {
10025 Definition* obj = StoreInto(use);
10026 if ((obj != NULL) && (obj != alloc)) {
10027 AddInstruction(&worklist_, obj);
10028 }
10029 }
10030 }
10031
10032
10033 void AllocationSinking::ExitsCollector::CollectTransitively(Definition* alloc) {
10034 exits_.TruncateTo(0);
10035 worklist_.TruncateTo(0);
10036
10037 worklist_.Add(alloc);
10038
10039 // Note: worklist potentially will grow while we are iterating over it.
10040 // We are not removing allocations from the worklist not to waste space on
10041 // the side maintaining BitVector of already processed allocations: worklist
10042 // is expected to be very small thus linear search in it is just as effecient
10043 // as a bitvector.
10044 for (intptr_t i = 0; i < worklist_.length(); i++) {
10045 Collect(worklist_[i]);
10046 }
10047 }
10048
10049
10050 void AllocationSinking::InsertMaterializations(Definition* alloc) {
10051 // Collect all fields that are written for this instance.
10052 ZoneGrowableArray<const Object*>* slots =
10053 new(I) ZoneGrowableArray<const Object*>(5);
10054
10055 for (Value* use = alloc->input_use_list();
10056 use != NULL;
10057 use = use->next_use()) {
10058 StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField();
10059 if ((store != NULL) && (store->instance()->definition() == alloc)) {
10060 if (!store->field().IsNull()) {
10061 AddSlot(slots, store->field());
10062 } else {
10063 AddSlot(slots, Smi::ZoneHandle(I, Smi::New(store->offset_in_bytes())));
10064 }
10065 }
10066 }
10067
10068 if (alloc->ArgumentCount() > 0) {
10069 AllocateObjectInstr* alloc_object = alloc->AsAllocateObject();
10070 ASSERT(alloc_object->ArgumentCount() == 1);
10071 intptr_t type_args_offset =
10072 alloc_object->cls().type_arguments_field_offset();
10073 AddSlot(slots, Smi::ZoneHandle(I, Smi::New(type_args_offset)));
10074 }
10075
10076 // Collect all instructions that mention this object in the environment.
10077 exits_collector_.CollectTransitively(alloc);
10078
10079 // Insert materializations at environment uses.
10080 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) {
10081 CreateMaterializationAt(
10082 exits_collector_.exits()[i], alloc, *slots);
10083 }
10084 }
10085
10086
10087 } // namespace dart 1638 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/constant_propagator.h ('k') | runtime/vm/flow_graph_optimizer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698