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