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