| 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/jit_optimizer.h" | 5 #include "vm/jit_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
| 9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
| 10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 | 54 |
| 55 // Optimize instance calls using cid. This is called after optimizer | 55 // Optimize instance calls using cid. This is called after optimizer |
| 56 // converted instance calls to instructions. Any remaining | 56 // converted instance calls to instructions. Any remaining |
| 57 // instance calls are either megamorphic calls, cannot be optimized or | 57 // instance calls are either megamorphic calls, cannot be optimized or |
| 58 // have no runtime type feedback collected. | 58 // have no runtime type feedback collected. |
| 59 // Attempts to convert an instance call (IC call) using propagated class-ids, | 59 // Attempts to convert an instance call (IC call) using propagated class-ids, |
| 60 // e.g., receiver class id, guarded-cid, or by guessing cid-s. | 60 // e.g., receiver class id, guarded-cid, or by guessing cid-s. |
| 61 void JitOptimizer::ApplyClassIds() { | 61 void JitOptimizer::ApplyClassIds() { |
| 62 ASSERT(current_iterator_ == NULL); | 62 ASSERT(current_iterator_ == NULL); |
| 63 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 63 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
| 64 !block_it.Done(); | 64 !block_it.Done(); block_it.Advance()) { |
| 65 block_it.Advance()) { | |
| 66 ForwardInstructionIterator it(block_it.Current()); | 65 ForwardInstructionIterator it(block_it.Current()); |
| 67 current_iterator_ = ⁢ | 66 current_iterator_ = ⁢ |
| 68 for (; !it.Done(); it.Advance()) { | 67 for (; !it.Done(); it.Advance()) { |
| 69 Instruction* instr = it.Current(); | 68 Instruction* instr = it.Current(); |
| 70 if (instr->IsInstanceCall()) { | 69 if (instr->IsInstanceCall()) { |
| 71 InstanceCallInstr* call = instr->AsInstanceCall(); | 70 InstanceCallInstr* call = instr->AsInstanceCall(); |
| 72 if (call->HasICData()) { | 71 if (call->HasICData()) { |
| 73 if (TryCreateICData(call)) { | 72 if (TryCreateICData(call)) { |
| 74 VisitInstanceCall(call); | 73 VisitInstanceCall(call); |
| 75 } | 74 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 98 } | 97 } |
| 99 | 98 |
| 100 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); | 99 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); |
| 101 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); | 100 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); |
| 102 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { | 101 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { |
| 103 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid()); | 102 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid()); |
| 104 } | 103 } |
| 105 | 104 |
| 106 const Token::Kind op_kind = call->token_kind(); | 105 const Token::Kind op_kind = call->token_kind(); |
| 107 if (Token::IsRelationalOperator(op_kind) || | 106 if (Token::IsRelationalOperator(op_kind) || |
| 108 Token::IsEqualityOperator(op_kind) || | 107 Token::IsEqualityOperator(op_kind) || Token::IsBinaryOperator(op_kind)) { |
| 109 Token::IsBinaryOperator(op_kind)) { | |
| 110 // Guess cid: if one of the inputs is a number assume that the other | 108 // Guess cid: if one of the inputs is a number assume that the other |
| 111 // is a number of same type. | 109 // is a number of same type. |
| 112 if (FLAG_guess_icdata_cid) { | 110 if (FLAG_guess_icdata_cid) { |
| 113 const intptr_t cid_0 = class_ids[0]; | 111 const intptr_t cid_0 = class_ids[0]; |
| 114 const intptr_t cid_1 = class_ids[1]; | 112 const intptr_t cid_1 = class_ids[1]; |
| 115 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) { | 113 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) { |
| 116 class_ids[0] = cid_1; | 114 class_ids[0] = cid_1; |
| 117 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) { | 115 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) { |
| 118 class_ids[1] = cid_0; | 116 class_ids[1] = cid_0; |
| 119 } | 117 } |
| 120 } | 118 } |
| 121 } | 119 } |
| 122 | 120 |
| 123 bool all_cids_known = true; | 121 bool all_cids_known = true; |
| 124 for (intptr_t i = 0; i < class_ids.length(); i++) { | 122 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 125 if (class_ids[i] == kDynamicCid) { | 123 if (class_ids[i] == kDynamicCid) { |
| 126 // Not all cid-s known. | 124 // Not all cid-s known. |
| 127 all_cids_known = false; | 125 all_cids_known = false; |
| 128 break; | 126 break; |
| 129 } | 127 } |
| 130 } | 128 } |
| 131 | 129 |
| 132 if (all_cids_known) { | 130 if (all_cids_known) { |
| 133 const Class& receiver_class = Class::Handle(Z, | 131 const Class& receiver_class = |
| 134 isolate()->class_table()->At(class_ids[0])); | 132 Class::Handle(Z, isolate()->class_table()->At(class_ids[0])); |
| 135 if (!receiver_class.is_finalized()) { | 133 if (!receiver_class.is_finalized()) { |
| 136 // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can | 134 // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can |
| 137 // cause class finalization, since callee's receiver class may not be | 135 // cause class finalization, since callee's receiver class may not be |
| 138 // finalized yet. | 136 // finalized yet. |
| 139 return false; | 137 return false; |
| 140 } | 138 } |
| 141 const Array& args_desc_array = Array::Handle(Z, | 139 const Array& args_desc_array = |
| 142 ArgumentsDescriptor::New(call->ArgumentCount(), | 140 Array::Handle(Z, ArgumentsDescriptor::New(call->ArgumentCount(), |
| 143 call->argument_names())); | 141 call->argument_names())); |
| 144 ArgumentsDescriptor args_desc(args_desc_array); | 142 ArgumentsDescriptor args_desc(args_desc_array); |
| 145 const Function& function = Function::Handle(Z, | 143 const Function& function = |
| 146 Resolver::ResolveDynamicForReceiverClass( | 144 Function::Handle(Z, Resolver::ResolveDynamicForReceiverClass( |
| 147 receiver_class, | 145 receiver_class, call->function_name(), |
| 148 call->function_name(), | 146 args_desc, false /* allow add */)); |
| 149 args_desc, | |
| 150 false /* allow add */)); | |
| 151 if (function.IsNull()) { | 147 if (function.IsNull()) { |
| 152 return false; | 148 return false; |
| 153 } | 149 } |
| 154 | 150 |
| 155 // Create new ICData, do not modify the one attached to the instruction | 151 // Create new ICData, do not modify the one attached to the instruction |
| 156 // since it is attached to the assembly instruction itself. | 152 // since it is attached to the assembly instruction itself. |
| 157 // TODO(srdjan): Prevent modification of ICData object that is | 153 // TODO(srdjan): Prevent modification of ICData object that is |
| 158 // referenced in assembly code. | 154 // referenced in assembly code. |
| 159 const ICData& ic_data = ICData::ZoneHandle(Z, | 155 const ICData& ic_data = ICData::ZoneHandle( |
| 160 ICData::NewFrom(*call->ic_data(), class_ids.length())); | 156 Z, ICData::NewFrom(*call->ic_data(), class_ids.length())); |
| 161 if (class_ids.length() > 1) { | 157 if (class_ids.length() > 1) { |
| 162 ic_data.AddCheck(class_ids, function); | 158 ic_data.AddCheck(class_ids, function); |
| 163 } else { | 159 } else { |
| 164 ASSERT(class_ids.length() == 1); | 160 ASSERT(class_ids.length() == 1); |
| 165 ic_data.AddReceiverCheck(class_ids[0], function); | 161 ic_data.AddReceiverCheck(class_ids[0], function); |
| 166 } | 162 } |
| 167 call->set_ic_data(&ic_data); | 163 call->set_ic_data(&ic_data); |
| 168 return true; | 164 return true; |
| 169 } | 165 } |
| 170 | 166 |
| 171 // Check if getter or setter in function's class and class is currently leaf. | 167 // Check if getter or setter in function's class and class is currently leaf. |
| 172 if (FLAG_guess_icdata_cid && | 168 if (FLAG_guess_icdata_cid && ((call->token_kind() == Token::kGET) || |
| 173 ((call->token_kind() == Token::kGET) || | 169 (call->token_kind() == Token::kSET))) { |
| 174 (call->token_kind() == Token::kSET))) { | |
| 175 const Class& owner_class = Class::Handle(Z, function().Owner()); | 170 const Class& owner_class = Class::Handle(Z, function().Owner()); |
| 176 if (!owner_class.is_abstract() && | 171 if (!owner_class.is_abstract() && !CHA::HasSubclasses(owner_class) && |
| 177 !CHA::HasSubclasses(owner_class) && | |
| 178 !CHA::IsImplemented(owner_class)) { | 172 !CHA::IsImplemented(owner_class)) { |
| 179 const Array& args_desc_array = Array::Handle(Z, | 173 const Array& args_desc_array = |
| 180 ArgumentsDescriptor::New(call->ArgumentCount(), | 174 Array::Handle(Z, ArgumentsDescriptor::New(call->ArgumentCount(), |
| 181 call->argument_names())); | 175 call->argument_names())); |
| 182 ArgumentsDescriptor args_desc(args_desc_array); | 176 ArgumentsDescriptor args_desc(args_desc_array); |
| 183 const Function& function = Function::Handle(Z, | 177 const Function& function = |
| 184 Resolver::ResolveDynamicForReceiverClass(owner_class, | 178 Function::Handle(Z, Resolver::ResolveDynamicForReceiverClass( |
| 185 call->function_name(), | 179 owner_class, call->function_name(), args_desc, |
| 186 args_desc, | 180 false /* allow_add */)); |
| 187 false /* allow_add */)); | |
| 188 if (!function.IsNull()) { | 181 if (!function.IsNull()) { |
| 189 const ICData& ic_data = ICData::ZoneHandle(Z, | 182 const ICData& ic_data = ICData::ZoneHandle( |
| 190 ICData::NewFrom(*call->ic_data(), class_ids.length())); | 183 Z, ICData::NewFrom(*call->ic_data(), class_ids.length())); |
| 191 ic_data.AddReceiverCheck(owner_class.id(), function); | 184 ic_data.AddReceiverCheck(owner_class.id(), function); |
| 192 call->set_ic_data(&ic_data); | 185 call->set_ic_data(&ic_data); |
| 193 return true; | 186 return true; |
| 194 } | 187 } |
| 195 } | 188 } |
| 196 } | 189 } |
| 197 | 190 |
| 198 return false; | 191 return false; |
| 199 } | 192 } |
| 200 | 193 |
| 201 | 194 |
| 202 const ICData& JitOptimizer::TrySpecializeICData(const ICData& ic_data, | 195 const ICData& JitOptimizer::TrySpecializeICData(const ICData& ic_data, |
| 203 intptr_t cid) { | 196 intptr_t cid) { |
| 204 ASSERT(ic_data.NumArgsTested() == 1); | 197 ASSERT(ic_data.NumArgsTested() == 1); |
| 205 | 198 |
| 206 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { | 199 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { |
| 207 return ic_data; // Nothing to do | 200 return ic_data; // Nothing to do |
| 208 } | 201 } |
| 209 | 202 |
| 210 const Function& function = | 203 const Function& function = |
| 211 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); | 204 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); |
| 212 // TODO(fschneider): Try looking up the function on the class if it is | 205 // TODO(fschneider): Try looking up the function on the class if it is |
| 213 // not found in the ICData. | 206 // not found in the ICData. |
| 214 if (!function.IsNull()) { | 207 if (!function.IsNull()) { |
| 215 const ICData& new_ic_data = ICData::ZoneHandle(Z, ICData::New( | 208 const ICData& new_ic_data = ICData::ZoneHandle( |
| 216 Function::Handle(Z, ic_data.Owner()), | 209 Z, ICData::New(Function::Handle(Z, ic_data.Owner()), |
| 217 String::Handle(Z, ic_data.target_name()), | 210 String::Handle(Z, ic_data.target_name()), |
| 218 Object::empty_array(), // Dummy argument descriptor. | 211 Object::empty_array(), // Dummy argument descriptor. |
| 219 ic_data.deopt_id(), | 212 ic_data.deopt_id(), ic_data.NumArgsTested(), false)); |
| 220 ic_data.NumArgsTested(), | |
| 221 false)); | |
| 222 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); | 213 new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); |
| 223 new_ic_data.AddReceiverCheck(cid, function); | 214 new_ic_data.AddReceiverCheck(cid, function); |
| 224 return new_ic_data; | 215 return new_ic_data; |
| 225 } | 216 } |
| 226 | 217 |
| 227 return ic_data; | 218 return ic_data; |
| 228 } | 219 } |
| 229 | 220 |
| 230 | 221 |
| 231 void JitOptimizer::SpecializePolymorphicInstanceCall( | 222 void JitOptimizer::SpecializePolymorphicInstanceCall( |
| (...skipping 14 matching lines...) Expand all Loading... |
| 246 | 237 |
| 247 const ICData& ic_data = TrySpecializeICData(call->ic_data(), receiver_cid); | 238 const ICData& ic_data = TrySpecializeICData(call->ic_data(), receiver_cid); |
| 248 if (ic_data.raw() == call->ic_data().raw()) { | 239 if (ic_data.raw() == call->ic_data().raw()) { |
| 249 // No specialization. | 240 // No specialization. |
| 250 return; | 241 return; |
| 251 } | 242 } |
| 252 | 243 |
| 253 const bool with_checks = false; | 244 const bool with_checks = false; |
| 254 const bool complete = false; | 245 const bool complete = false; |
| 255 PolymorphicInstanceCallInstr* specialized = | 246 PolymorphicInstanceCallInstr* specialized = |
| 256 new(Z) PolymorphicInstanceCallInstr(call->instance_call(), | 247 new (Z) PolymorphicInstanceCallInstr(call->instance_call(), ic_data, |
| 257 ic_data, | 248 with_checks, complete); |
| 258 with_checks, | |
| 259 complete); | |
| 260 call->ReplaceWith(specialized, current_iterator()); | 249 call->ReplaceWith(specialized, current_iterator()); |
| 261 } | 250 } |
| 262 | 251 |
| 263 | 252 |
| 264 static bool ClassIdIsOneOf(intptr_t class_id, | 253 static bool ClassIdIsOneOf(intptr_t class_id, |
| 265 const GrowableArray<intptr_t>& class_ids) { | 254 const GrowableArray<intptr_t>& class_ids) { |
| 266 for (intptr_t i = 0; i < class_ids.length(); i++) { | 255 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 267 ASSERT(class_ids[i] != kIllegalCid); | 256 ASSERT(class_ids[i] != kIllegalCid); |
| 268 if (class_ids[i] == class_id) { | 257 if (class_ids[i] == class_id) { |
| 269 return true; | 258 return true; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 314 (class_ids[1] == argument_class_id)) { | 303 (class_ids[1] == argument_class_id)) { |
| 315 return true; | 304 return true; |
| 316 } | 305 } |
| 317 } | 306 } |
| 318 } | 307 } |
| 319 return false; | 308 return false; |
| 320 } | 309 } |
| 321 | 310 |
| 322 | 311 |
| 323 static bool HasOnlyOneSmi(const ICData& ic_data) { | 312 static bool HasOnlyOneSmi(const ICData& ic_data) { |
| 324 return (ic_data.NumberOfUsedChecks() == 1) | 313 return (ic_data.NumberOfUsedChecks() == 1) && |
| 325 && ic_data.HasReceiverClassId(kSmiCid); | 314 ic_data.HasReceiverClassId(kSmiCid); |
| 326 } | 315 } |
| 327 | 316 |
| 328 | 317 |
| 329 static bool HasOnlySmiOrMint(const ICData& ic_data) { | 318 static bool HasOnlySmiOrMint(const ICData& ic_data) { |
| 330 if (ic_data.NumberOfUsedChecks() == 1) { | 319 if (ic_data.NumberOfUsedChecks() == 1) { |
| 331 return ic_data.HasReceiverClassId(kSmiCid) | 320 return ic_data.HasReceiverClassId(kSmiCid) || |
| 332 || ic_data.HasReceiverClassId(kMintCid); | 321 ic_data.HasReceiverClassId(kMintCid); |
| 333 } | 322 } |
| 334 return (ic_data.NumberOfUsedChecks() == 2) | 323 return (ic_data.NumberOfUsedChecks() == 2) && |
| 335 && ic_data.HasReceiverClassId(kSmiCid) | 324 ic_data.HasReceiverClassId(kSmiCid) && |
| 336 && ic_data.HasReceiverClassId(kMintCid); | 325 ic_data.HasReceiverClassId(kMintCid); |
| 337 } | 326 } |
| 338 | 327 |
| 339 | 328 |
| 340 static bool HasOnlyTwoOf(const ICData& ic_data, intptr_t cid) { | 329 static bool HasOnlyTwoOf(const ICData& ic_data, intptr_t cid) { |
| 341 if (ic_data.NumberOfUsedChecks() != 1) { | 330 if (ic_data.NumberOfUsedChecks() != 1) { |
| 342 return false; | 331 return false; |
| 343 } | 332 } |
| 344 GrowableArray<intptr_t> first; | 333 GrowableArray<intptr_t> first; |
| 345 GrowableArray<intptr_t> second; | 334 GrowableArray<intptr_t> second; |
| 346 ic_data.GetUsedCidsForTwoArgs(&first, &second); | 335 ic_data.GetUsedCidsForTwoArgs(&first, &second); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 369 // of Double and Smi for the receiver and argument classes. | 358 // of Double and Smi for the receiver and argument classes. |
| 370 static bool HasTwoDoubleOrSmi(const ICData& ic_data) { | 359 static bool HasTwoDoubleOrSmi(const ICData& ic_data) { |
| 371 GrowableArray<intptr_t> class_ids(2); | 360 GrowableArray<intptr_t> class_ids(2); |
| 372 class_ids.Add(kSmiCid); | 361 class_ids.Add(kSmiCid); |
| 373 class_ids.Add(kDoubleCid); | 362 class_ids.Add(kDoubleCid); |
| 374 return ICDataHasOnlyReceiverArgumentClassIds(ic_data, class_ids, class_ids); | 363 return ICDataHasOnlyReceiverArgumentClassIds(ic_data, class_ids, class_ids); |
| 375 } | 364 } |
| 376 | 365 |
| 377 | 366 |
| 378 static bool HasOnlyOneDouble(const ICData& ic_data) { | 367 static bool HasOnlyOneDouble(const ICData& ic_data) { |
| 379 return (ic_data.NumberOfUsedChecks() == 1) | 368 return (ic_data.NumberOfUsedChecks() == 1) && |
| 380 && ic_data.HasReceiverClassId(kDoubleCid); | 369 ic_data.HasReceiverClassId(kDoubleCid); |
| 381 } | 370 } |
| 382 | 371 |
| 383 | 372 |
| 384 static bool ShouldSpecializeForDouble(const ICData& ic_data) { | 373 static bool ShouldSpecializeForDouble(const ICData& ic_data) { |
| 385 // Don't specialize for double if we can't unbox them. | 374 // Don't specialize for double if we can't unbox them. |
| 386 if (!CanUnboxDouble()) { | 375 if (!CanUnboxDouble()) { |
| 387 return false; | 376 return false; |
| 388 } | 377 } |
| 389 | 378 |
| 390 // Unboxed double operation can't handle case of two smis. | 379 // Unboxed double operation can't handle case of two smis. |
| 391 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | 380 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
| 392 return false; | 381 return false; |
| 393 } | 382 } |
| 394 | 383 |
| 395 // Check that it have seen only smis and doubles. | 384 // Check that it have seen only smis and doubles. |
| 396 return HasTwoDoubleOrSmi(ic_data); | 385 return HasTwoDoubleOrSmi(ic_data); |
| 397 } | 386 } |
| 398 | 387 |
| 399 | 388 |
| 400 void JitOptimizer::ReplaceCall(Definition* call, | 389 void JitOptimizer::ReplaceCall(Definition* call, Definition* replacement) { |
| 401 Definition* replacement) { | |
| 402 // Remove the original push arguments. | 390 // Remove the original push arguments. |
| 403 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 391 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 404 PushArgumentInstr* push = call->PushArgumentAt(i); | 392 PushArgumentInstr* push = call->PushArgumentAt(i); |
| 405 push->ReplaceUsesWith(push->value()->definition()); | 393 push->ReplaceUsesWith(push->value()->definition()); |
| 406 push->RemoveFromGraph(); | 394 push->RemoveFromGraph(); |
| 407 } | 395 } |
| 408 call->ReplaceWith(replacement, current_iterator()); | 396 call->ReplaceWith(replacement, current_iterator()); |
| 409 } | 397 } |
| 410 | 398 |
| 411 | 399 |
| 412 void JitOptimizer::AddCheckSmi(Definition* to_check, | 400 void JitOptimizer::AddCheckSmi(Definition* to_check, |
| 413 intptr_t deopt_id, | 401 intptr_t deopt_id, |
| 414 Environment* deopt_environment, | 402 Environment* deopt_environment, |
| 415 Instruction* insert_before) { | 403 Instruction* insert_before) { |
| 416 if (to_check->Type()->ToCid() != kSmiCid) { | 404 if (to_check->Type()->ToCid() != kSmiCid) { |
| 417 InsertBefore(insert_before, | 405 InsertBefore(insert_before, |
| 418 new(Z) CheckSmiInstr(new(Z) Value(to_check), | 406 new (Z) CheckSmiInstr(new (Z) Value(to_check), deopt_id, |
| 419 deopt_id, | 407 insert_before->token_pos()), |
| 420 insert_before->token_pos()), | 408 deopt_environment, FlowGraph::kEffect); |
| 421 deopt_environment, | |
| 422 FlowGraph::kEffect); | |
| 423 } | 409 } |
| 424 } | 410 } |
| 425 | 411 |
| 426 | 412 |
| 427 Instruction* JitOptimizer::GetCheckClass(Definition* to_check, | 413 Instruction* JitOptimizer::GetCheckClass(Definition* to_check, |
| 428 const ICData& unary_checks, | 414 const ICData& unary_checks, |
| 429 intptr_t deopt_id, | 415 intptr_t deopt_id, |
| 430 TokenPosition token_pos) { | 416 TokenPosition token_pos) { |
| 431 if ((unary_checks.NumberOfUsedChecks() == 1) && | 417 if ((unary_checks.NumberOfUsedChecks() == 1) && |
| 432 unary_checks.HasReceiverClassId(kSmiCid)) { | 418 unary_checks.HasReceiverClassId(kSmiCid)) { |
| 433 return new(Z) CheckSmiInstr(new(Z) Value(to_check), | 419 return new (Z) CheckSmiInstr(new (Z) Value(to_check), deopt_id, token_pos); |
| 434 deopt_id, | |
| 435 token_pos); | |
| 436 } | 420 } |
| 437 return new(Z) CheckClassInstr( | 421 return new (Z) CheckClassInstr(new (Z) Value(to_check), deopt_id, |
| 438 new(Z) Value(to_check), deopt_id, unary_checks, token_pos); | 422 unary_checks, token_pos); |
| 439 } | 423 } |
| 440 | 424 |
| 441 | 425 |
| 442 void JitOptimizer::AddCheckClass(Definition* to_check, | 426 void JitOptimizer::AddCheckClass(Definition* to_check, |
| 443 const ICData& unary_checks, | 427 const ICData& unary_checks, |
| 444 intptr_t deopt_id, | 428 intptr_t deopt_id, |
| 445 Environment* deopt_environment, | 429 Environment* deopt_environment, |
| 446 Instruction* insert_before) { | 430 Instruction* insert_before) { |
| 447 // Type propagation has not run yet, we cannot eliminate the check. | 431 // Type propagation has not run yet, we cannot eliminate the check. |
| 448 Instruction* check = GetCheckClass( | 432 Instruction* check = GetCheckClass(to_check, unary_checks, deopt_id, |
| 449 to_check, unary_checks, deopt_id, insert_before->token_pos()); | 433 insert_before->token_pos()); |
| 450 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); | 434 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); |
| 451 } | 435 } |
| 452 | 436 |
| 453 | 437 |
| 454 void JitOptimizer::AddReceiverCheck(InstanceCallInstr* call) { | 438 void JitOptimizer::AddReceiverCheck(InstanceCallInstr* call) { |
| 455 AddCheckClass(call->ArgumentAt(0), | 439 AddCheckClass(call->ArgumentAt(0), |
| 456 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), | 440 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), |
| 457 call->deopt_id(), | 441 call->deopt_id(), call->env(), call); |
| 458 call->env(), | |
| 459 call); | |
| 460 } | 442 } |
| 461 | 443 |
| 462 | 444 |
| 463 static bool ArgIsAlways(intptr_t cid, | 445 static bool ArgIsAlways(intptr_t cid, |
| 464 const ICData& ic_data, | 446 const ICData& ic_data, |
| 465 intptr_t arg_number) { | 447 intptr_t arg_number) { |
| 466 ASSERT(ic_data.NumArgsTested() > arg_number); | 448 ASSERT(ic_data.NumArgsTested() > arg_number); |
| 467 if (ic_data.NumberOfUsedChecks() == 0) { | 449 if (ic_data.NumberOfUsedChecks() == 0) { |
| 468 return false; | 450 return false; |
| 469 } | 451 } |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 } | 509 } |
| 528 if (IsLengthOneString(left)) { | 510 if (IsLengthOneString(left)) { |
| 529 // Optimize if left is a string with length one (either constant or | 511 // Optimize if left is a string with length one (either constant or |
| 530 // result of string-from-char-code. | 512 // result of string-from-char-code. |
| 531 if (left->IsConstant()) { | 513 if (left->IsConstant()) { |
| 532 ConstantInstr* left_const = left->AsConstant(); | 514 ConstantInstr* left_const = left->AsConstant(); |
| 533 const String& str = String::Cast(left_const->value()); | 515 const String& str = String::Cast(left_const->value()); |
| 534 ASSERT(str.Length() == 1); | 516 ASSERT(str.Length() == 1); |
| 535 ConstantInstr* char_code_left = flow_graph()->GetConstant( | 517 ConstantInstr* char_code_left = flow_graph()->GetConstant( |
| 536 Smi::ZoneHandle(Z, Smi::New(static_cast<intptr_t>(str.CharAt(0))))); | 518 Smi::ZoneHandle(Z, Smi::New(static_cast<intptr_t>(str.CharAt(0))))); |
| 537 left_val = new(Z) Value(char_code_left); | 519 left_val = new (Z) Value(char_code_left); |
| 538 } else if (left->IsOneByteStringFromCharCode()) { | 520 } else if (left->IsOneByteStringFromCharCode()) { |
| 539 // Use input of string-from-charcode as left value. | 521 // Use input of string-from-charcode as left value. |
| 540 OneByteStringFromCharCodeInstr* instr = | 522 OneByteStringFromCharCodeInstr* instr = |
| 541 left->AsOneByteStringFromCharCode(); | 523 left->AsOneByteStringFromCharCode(); |
| 542 left_val = new(Z) Value(instr->char_code()->definition()); | 524 left_val = new (Z) Value(instr->char_code()->definition()); |
| 543 to_remove_left = instr; | 525 to_remove_left = instr; |
| 544 } else { | 526 } else { |
| 545 // IsLengthOneString(left) should have been false. | 527 // IsLengthOneString(left) should have been false. |
| 546 UNREACHABLE(); | 528 UNREACHABLE(); |
| 547 } | 529 } |
| 548 | 530 |
| 549 Definition* to_remove_right = NULL; | 531 Definition* to_remove_right = NULL; |
| 550 Value* right_val = NULL; | 532 Value* right_val = NULL; |
| 551 if (right->IsOneByteStringFromCharCode()) { | 533 if (right->IsOneByteStringFromCharCode()) { |
| 552 // Skip string-from-char-code, and use its input as right value. | 534 // Skip string-from-char-code, and use its input as right value. |
| 553 OneByteStringFromCharCodeInstr* right_instr = | 535 OneByteStringFromCharCodeInstr* right_instr = |
| 554 right->AsOneByteStringFromCharCode(); | 536 right->AsOneByteStringFromCharCode(); |
| 555 right_val = new(Z) Value(right_instr->char_code()->definition()); | 537 right_val = new (Z) Value(right_instr->char_code()->definition()); |
| 556 to_remove_right = right_instr; | 538 to_remove_right = right_instr; |
| 557 } else { | 539 } else { |
| 558 const ICData& unary_checks_1 = | 540 const ICData& unary_checks_1 = |
| 559 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); | 541 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); |
| 560 AddCheckClass(right, | 542 AddCheckClass(right, unary_checks_1, call->deopt_id(), call->env(), call); |
| 561 unary_checks_1, | |
| 562 call->deopt_id(), | |
| 563 call->env(), | |
| 564 call); | |
| 565 // String-to-char-code instructions returns -1 (illegal charcode) if | 543 // String-to-char-code instructions returns -1 (illegal charcode) if |
| 566 // string is not of length one. | 544 // string is not of length one. |
| 567 StringToCharCodeInstr* char_code_right = | 545 StringToCharCodeInstr* char_code_right = new (Z) |
| 568 new(Z) StringToCharCodeInstr(new(Z) Value(right), kOneByteStringCid); | 546 StringToCharCodeInstr(new (Z) Value(right), kOneByteStringCid); |
| 569 InsertBefore(call, char_code_right, call->env(), FlowGraph::kValue); | 547 InsertBefore(call, char_code_right, call->env(), FlowGraph::kValue); |
| 570 right_val = new(Z) Value(char_code_right); | 548 right_val = new (Z) Value(char_code_right); |
| 571 } | 549 } |
| 572 | 550 |
| 573 // Comparing char-codes instead of strings. | 551 // Comparing char-codes instead of strings. |
| 574 EqualityCompareInstr* comp = | 552 EqualityCompareInstr* comp = |
| 575 new(Z) EqualityCompareInstr(call->token_pos(), | 553 new (Z) EqualityCompareInstr(call->token_pos(), op_kind, left_val, |
| 576 op_kind, | 554 right_val, kSmiCid, call->deopt_id()); |
| 577 left_val, | |
| 578 right_val, | |
| 579 kSmiCid, | |
| 580 call->deopt_id()); | |
| 581 ReplaceCall(call, comp); | 555 ReplaceCall(call, comp); |
| 582 | 556 |
| 583 // Remove dead instructions. | 557 // Remove dead instructions. |
| 584 if ((to_remove_left != NULL) && | 558 if ((to_remove_left != NULL) && |
| 585 (to_remove_left->input_use_list() == NULL)) { | 559 (to_remove_left->input_use_list() == NULL)) { |
| 586 to_remove_left->ReplaceUsesWith(flow_graph()->constant_null()); | 560 to_remove_left->ReplaceUsesWith(flow_graph()->constant_null()); |
| 587 to_remove_left->RemoveFromGraph(); | 561 to_remove_left->RemoveFromGraph(); |
| 588 } | 562 } |
| 589 if ((to_remove_right != NULL) && | 563 if ((to_remove_right != NULL) && |
| 590 (to_remove_right->input_use_list() == NULL)) { | 564 (to_remove_right->input_use_list() == NULL)) { |
| 591 to_remove_right->ReplaceUsesWith(flow_graph()->constant_null()); | 565 to_remove_right->ReplaceUsesWith(flow_graph()->constant_null()); |
| 592 to_remove_right->RemoveFromGraph(); | 566 to_remove_right->RemoveFromGraph(); |
| 593 } | 567 } |
| 594 return true; | 568 return true; |
| 595 } | 569 } |
| 596 return false; | 570 return false; |
| 597 } | 571 } |
| 598 | 572 |
| 599 | 573 |
| 600 static bool SmiFitsInDouble() { return kSmiBits < 53; } | 574 static bool SmiFitsInDouble() { |
| 575 return kSmiBits < 53; |
| 576 } |
| 601 | 577 |
| 602 bool JitOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, | 578 bool JitOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
| 603 Token::Kind op_kind) { | 579 Token::Kind op_kind) { |
| 604 const ICData& ic_data = *call->ic_data(); | 580 const ICData& ic_data = *call->ic_data(); |
| 605 ASSERT(ic_data.NumArgsTested() == 2); | 581 ASSERT(ic_data.NumArgsTested() == 2); |
| 606 | 582 |
| 607 ASSERT(call->ArgumentCount() == 2); | 583 ASSERT(call->ArgumentCount() == 2); |
| 608 Definition* left = call->ArgumentAt(0); | 584 Definition* left = call->ArgumentAt(0); |
| 609 Definition* right = call->ArgumentAt(1); | 585 Definition* right = call->ArgumentAt(1); |
| 610 | 586 |
| 611 intptr_t cid = kIllegalCid; | 587 intptr_t cid = kIllegalCid; |
| 612 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { | 588 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { |
| 613 if (TryStringLengthOneEquality(call, op_kind)) { | 589 if (TryStringLengthOneEquality(call, op_kind)) { |
| 614 return true; | 590 return true; |
| 615 } else { | 591 } else { |
| 616 return false; | 592 return false; |
| 617 } | 593 } |
| 618 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 594 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 619 InsertBefore(call, | 595 InsertBefore(call, |
| 620 new(Z) CheckSmiInstr(new(Z) Value(left), | 596 new (Z) CheckSmiInstr(new (Z) Value(left), call->deopt_id(), |
| 621 call->deopt_id(), | 597 call->token_pos()), |
| 622 call->token_pos()), | 598 call->env(), FlowGraph::kEffect); |
| 623 call->env(), | |
| 624 FlowGraph::kEffect); | |
| 625 InsertBefore(call, | 599 InsertBefore(call, |
| 626 new(Z) CheckSmiInstr(new(Z) Value(right), | 600 new (Z) CheckSmiInstr(new (Z) Value(right), call->deopt_id(), |
| 627 call->deopt_id(), | 601 call->token_pos()), |
| 628 call->token_pos()), | 602 call->env(), FlowGraph::kEffect); |
| 629 call->env(), | |
| 630 FlowGraph::kEffect); | |
| 631 cid = kSmiCid; | 603 cid = kSmiCid; |
| 632 } else if (HasTwoMintOrSmi(ic_data) && | 604 } else if (HasTwoMintOrSmi(ic_data) && |
| 633 FlowGraphCompiler::SupportsUnboxedMints()) { | 605 FlowGraphCompiler::SupportsUnboxedMints()) { |
| 634 cid = kMintCid; | 606 cid = kMintCid; |
| 635 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) { | 607 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) { |
| 636 // Use double comparison. | 608 // Use double comparison. |
| 637 if (SmiFitsInDouble()) { | 609 if (SmiFitsInDouble()) { |
| 638 cid = kDoubleCid; | 610 cid = kDoubleCid; |
| 639 } else { | 611 } else { |
| 640 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | 612 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
| 641 // We cannot use double comparison on two smis. Need polymorphic | 613 // We cannot use double comparison on two smis. Need polymorphic |
| 642 // call. | 614 // call. |
| 643 return false; | 615 return false; |
| 644 } else { | 616 } else { |
| 645 InsertBefore(call, | 617 InsertBefore(call, new (Z) CheckEitherNonSmiInstr(new (Z) Value(left), |
| 646 new(Z) CheckEitherNonSmiInstr( | 618 new (Z) Value(right), |
| 647 new(Z) Value(left), | 619 call->deopt_id()), |
| 648 new(Z) Value(right), | 620 call->env(), FlowGraph::kEffect); |
| 649 call->deopt_id()), | |
| 650 call->env(), | |
| 651 FlowGraph::kEffect); | |
| 652 cid = kDoubleCid; | 621 cid = kDoubleCid; |
| 653 } | 622 } |
| 654 } | 623 } |
| 655 } else { | 624 } else { |
| 656 // Check if ICDData contains checks with Smi/Null combinations. In that case | 625 // Check if ICDData contains checks with Smi/Null combinations. In that case |
| 657 // we can still emit the optimized Smi equality operation but need to add | 626 // we can still emit the optimized Smi equality operation but need to add |
| 658 // checks for null or Smi. | 627 // checks for null or Smi. |
| 659 GrowableArray<intptr_t> smi_or_null(2); | 628 GrowableArray<intptr_t> smi_or_null(2); |
| 660 smi_or_null.Add(kSmiCid); | 629 smi_or_null.Add(kSmiCid); |
| 661 smi_or_null.Add(kNullCid); | 630 smi_or_null.Add(kNullCid); |
| 662 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data, | 631 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data, smi_or_null, |
| 663 smi_or_null, | |
| 664 smi_or_null)) { | 632 smi_or_null)) { |
| 665 const ICData& unary_checks_0 = | 633 const ICData& unary_checks_0 = |
| 666 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); | 634 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); |
| 667 AddCheckClass(left, | 635 AddCheckClass(left, unary_checks_0, call->deopt_id(), call->env(), call); |
| 668 unary_checks_0, | |
| 669 call->deopt_id(), | |
| 670 call->env(), | |
| 671 call); | |
| 672 | 636 |
| 673 const ICData& unary_checks_1 = | 637 const ICData& unary_checks_1 = |
| 674 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); | 638 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); |
| 675 AddCheckClass(right, | 639 AddCheckClass(right, unary_checks_1, call->deopt_id(), call->env(), call); |
| 676 unary_checks_1, | |
| 677 call->deopt_id(), | |
| 678 call->env(), | |
| 679 call); | |
| 680 cid = kSmiCid; | 640 cid = kSmiCid; |
| 681 } else { | 641 } else { |
| 682 // Shortcut for equality with null. | 642 // Shortcut for equality with null. |
| 683 ConstantInstr* right_const = right->AsConstant(); | 643 ConstantInstr* right_const = right->AsConstant(); |
| 684 ConstantInstr* left_const = left->AsConstant(); | 644 ConstantInstr* left_const = left->AsConstant(); |
| 685 if ((right_const != NULL && right_const->value().IsNull()) || | 645 if ((right_const != NULL && right_const->value().IsNull()) || |
| 686 (left_const != NULL && left_const->value().IsNull())) { | 646 (left_const != NULL && left_const->value().IsNull())) { |
| 687 StrictCompareInstr* comp = | 647 StrictCompareInstr* comp = new (Z) |
| 688 new(Z) StrictCompareInstr(call->token_pos(), | 648 StrictCompareInstr(call->token_pos(), Token::kEQ_STRICT, |
| 689 Token::kEQ_STRICT, | 649 new (Z) Value(left), new (Z) Value(right), |
| 690 new(Z) Value(left), | 650 false); // No number check. |
| 691 new(Z) Value(right), | |
| 692 false); // No number check. | |
| 693 ReplaceCall(call, comp); | 651 ReplaceCall(call, comp); |
| 694 return true; | 652 return true; |
| 695 } | 653 } |
| 696 return false; | 654 return false; |
| 697 } | 655 } |
| 698 } | 656 } |
| 699 ASSERT(cid != kIllegalCid); | 657 ASSERT(cid != kIllegalCid); |
| 700 EqualityCompareInstr* comp = new(Z) EqualityCompareInstr(call->token_pos(), | 658 EqualityCompareInstr* comp = new (Z) |
| 701 op_kind, | 659 EqualityCompareInstr(call->token_pos(), op_kind, new (Z) Value(left), |
| 702 new(Z) Value(left), | 660 new (Z) Value(right), cid, call->deopt_id()); |
| 703 new(Z) Value(right), | |
| 704 cid, | |
| 705 call->deopt_id()); | |
| 706 ReplaceCall(call, comp); | 661 ReplaceCall(call, comp); |
| 707 return true; | 662 return true; |
| 708 } | 663 } |
| 709 | 664 |
| 710 | 665 |
| 711 bool JitOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, | 666 bool JitOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, |
| 712 Token::Kind op_kind) { | 667 Token::Kind op_kind) { |
| 713 const ICData& ic_data = *call->ic_data(); | 668 const ICData& ic_data = *call->ic_data(); |
| 714 ASSERT(ic_data.NumArgsTested() == 2); | 669 ASSERT(ic_data.NumArgsTested() == 2); |
| 715 | 670 |
| 716 ASSERT(call->ArgumentCount() == 2); | 671 ASSERT(call->ArgumentCount() == 2); |
| 717 Definition* left = call->ArgumentAt(0); | 672 Definition* left = call->ArgumentAt(0); |
| 718 Definition* right = call->ArgumentAt(1); | 673 Definition* right = call->ArgumentAt(1); |
| 719 | 674 |
| 720 intptr_t cid = kIllegalCid; | 675 intptr_t cid = kIllegalCid; |
| 721 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 676 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 722 InsertBefore(call, | 677 InsertBefore(call, |
| 723 new(Z) CheckSmiInstr(new(Z) Value(left), | 678 new (Z) CheckSmiInstr(new (Z) Value(left), call->deopt_id(), |
| 724 call->deopt_id(), | 679 call->token_pos()), |
| 725 call->token_pos()), | 680 call->env(), FlowGraph::kEffect); |
| 726 call->env(), | |
| 727 FlowGraph::kEffect); | |
| 728 InsertBefore(call, | 681 InsertBefore(call, |
| 729 new(Z) CheckSmiInstr(new(Z) Value(right), | 682 new (Z) CheckSmiInstr(new (Z) Value(right), call->deopt_id(), |
| 730 call->deopt_id(), | 683 call->token_pos()), |
| 731 call->token_pos()), | 684 call->env(), FlowGraph::kEffect); |
| 732 call->env(), | |
| 733 FlowGraph::kEffect); | |
| 734 cid = kSmiCid; | 685 cid = kSmiCid; |
| 735 } else if (HasTwoMintOrSmi(ic_data) && | 686 } else if (HasTwoMintOrSmi(ic_data) && |
| 736 FlowGraphCompiler::SupportsUnboxedMints()) { | 687 FlowGraphCompiler::SupportsUnboxedMints()) { |
| 737 cid = kMintCid; | 688 cid = kMintCid; |
| 738 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) { | 689 } else if (HasTwoDoubleOrSmi(ic_data) && CanUnboxDouble()) { |
| 739 // Use double comparison. | 690 // Use double comparison. |
| 740 if (SmiFitsInDouble()) { | 691 if (SmiFitsInDouble()) { |
| 741 cid = kDoubleCid; | 692 cid = kDoubleCid; |
| 742 } else { | 693 } else { |
| 743 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | 694 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
| 744 // We cannot use double comparison on two smis. Need polymorphic | 695 // We cannot use double comparison on two smis. Need polymorphic |
| 745 // call. | 696 // call. |
| 746 return false; | 697 return false; |
| 747 } else { | 698 } else { |
| 748 InsertBefore(call, | 699 InsertBefore(call, new (Z) CheckEitherNonSmiInstr(new (Z) Value(left), |
| 749 new(Z) CheckEitherNonSmiInstr( | 700 new (Z) Value(right), |
| 750 new(Z) Value(left), | 701 call->deopt_id()), |
| 751 new(Z) Value(right), | 702 call->env(), FlowGraph::kEffect); |
| 752 call->deopt_id()), | |
| 753 call->env(), | |
| 754 FlowGraph::kEffect); | |
| 755 cid = kDoubleCid; | 703 cid = kDoubleCid; |
| 756 } | 704 } |
| 757 } | 705 } |
| 758 } else { | 706 } else { |
| 759 return false; | 707 return false; |
| 760 } | 708 } |
| 761 ASSERT(cid != kIllegalCid); | 709 ASSERT(cid != kIllegalCid); |
| 762 RelationalOpInstr* comp = new(Z) RelationalOpInstr(call->token_pos(), | 710 RelationalOpInstr* comp = |
| 763 op_kind, | 711 new (Z) RelationalOpInstr(call->token_pos(), op_kind, new (Z) Value(left), |
| 764 new(Z) Value(left), | 712 new (Z) Value(right), cid, call->deopt_id()); |
| 765 new(Z) Value(right), | |
| 766 cid, | |
| 767 call->deopt_id()); | |
| 768 ReplaceCall(call, comp); | 713 ReplaceCall(call, comp); |
| 769 return true; | 714 return true; |
| 770 } | 715 } |
| 771 | 716 |
| 772 | 717 |
| 773 bool JitOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, | 718 bool JitOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, |
| 774 Token::Kind op_kind) { | 719 Token::Kind op_kind) { |
| 775 intptr_t operands_type = kIllegalCid; | 720 intptr_t operands_type = kIllegalCid; |
| 776 ASSERT(call->HasICData()); | 721 ASSERT(call->HasICData()); |
| 777 const ICData& ic_data = *call->ic_data(); | 722 const ICData& ic_data = *call->ic_data(); |
| 778 switch (op_kind) { | 723 switch (op_kind) { |
| 779 case Token::kADD: | 724 case Token::kADD: |
| 780 case Token::kSUB: | 725 case Token::kSUB: |
| 781 case Token::kMUL: | 726 case Token::kMUL: |
| 782 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 727 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 783 // Don't generate smi code if the IC data is marked because | 728 // Don't generate smi code if the IC data is marked because |
| 784 // of an overflow. | 729 // of an overflow. |
| 785 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp) | 730 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp) |
| 786 ? kMintCid | 731 ? kMintCid |
| 787 : kSmiCid; | 732 : kSmiCid; |
| 788 } else if (HasTwoMintOrSmi(ic_data) && | 733 } else if (HasTwoMintOrSmi(ic_data) && |
| 789 FlowGraphCompiler::SupportsUnboxedMints()) { | 734 FlowGraphCompiler::SupportsUnboxedMints()) { |
| 790 // Don't generate mint code if the IC data is marked because of an | 735 // Don't generate mint code if the IC data is marked because of an |
| 791 // overflow. | 736 // overflow. |
| 792 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) return false; | 737 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) return false; |
| 793 operands_type = kMintCid; | 738 operands_type = kMintCid; |
| 794 } else if (ShouldSpecializeForDouble(ic_data)) { | 739 } else if (ShouldSpecializeForDouble(ic_data)) { |
| 795 operands_type = kDoubleCid; | 740 operands_type = kDoubleCid; |
| 796 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) { | 741 } else if (HasOnlyTwoOf(ic_data, kFloat32x4Cid)) { |
| 797 operands_type = kFloat32x4Cid; | 742 operands_type = kFloat32x4Cid; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 case Token::kSHR: | 778 case Token::kSHR: |
| 834 case Token::kSHL: | 779 case Token::kSHL: |
| 835 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 780 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 836 // Left shift may overflow from smi into mint or big ints. | 781 // Left shift may overflow from smi into mint or big ints. |
| 837 // Don't generate smi code if the IC data is marked because | 782 // Don't generate smi code if the IC data is marked because |
| 838 // of an overflow. | 783 // of an overflow. |
| 839 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) { | 784 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) { |
| 840 return false; | 785 return false; |
| 841 } | 786 } |
| 842 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp) | 787 operands_type = ic_data.HasDeoptReason(ICData::kDeoptBinarySmiOp) |
| 843 ? kMintCid | 788 ? kMintCid |
| 844 : kSmiCid; | 789 : kSmiCid; |
| 845 } else if (HasTwoMintOrSmi(ic_data) && | 790 } else if (HasTwoMintOrSmi(ic_data) && |
| 846 HasOnlyOneSmi(ICData::Handle(Z, | 791 HasOnlyOneSmi(ICData::Handle( |
| 847 ic_data.AsUnaryClassChecksForArgNr(1)))) { | 792 Z, ic_data.AsUnaryClassChecksForArgNr(1)))) { |
| 848 // Don't generate mint code if the IC data is marked because of an | 793 // Don't generate mint code if the IC data is marked because of an |
| 849 // overflow. | 794 // overflow. |
| 850 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) { | 795 if (ic_data.HasDeoptReason(ICData::kDeoptBinaryMintOp)) { |
| 851 return false; | 796 return false; |
| 852 } | 797 } |
| 853 // Check for smi/mint << smi or smi/mint >> smi. | 798 // Check for smi/mint << smi or smi/mint >> smi. |
| 854 operands_type = kMintCid; | 799 operands_type = kMintCid; |
| 855 } else { | 800 } else { |
| 856 return false; | 801 return false; |
| 857 } | 802 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 876 Definition* left = call->ArgumentAt(0); | 821 Definition* left = call->ArgumentAt(0); |
| 877 Definition* right = call->ArgumentAt(1); | 822 Definition* right = call->ArgumentAt(1); |
| 878 if (operands_type == kDoubleCid) { | 823 if (operands_type == kDoubleCid) { |
| 879 if (!CanUnboxDouble()) { | 824 if (!CanUnboxDouble()) { |
| 880 return false; | 825 return false; |
| 881 } | 826 } |
| 882 // Check that either left or right are not a smi. Result of a | 827 // Check that either left or right are not a smi. Result of a |
| 883 // binary operation with two smis is a smi not a double, except '/' which | 828 // binary operation with two smis is a smi not a double, except '/' which |
| 884 // returns a double for two smis. | 829 // returns a double for two smis. |
| 885 if (op_kind != Token::kDIV) { | 830 if (op_kind != Token::kDIV) { |
| 886 InsertBefore(call, | 831 InsertBefore(call, new (Z) CheckEitherNonSmiInstr(new (Z) Value(left), |
| 887 new(Z) CheckEitherNonSmiInstr( | 832 new (Z) Value(right), |
| 888 new(Z) Value(left), | 833 call->deopt_id()), |
| 889 new(Z) Value(right), | 834 call->env(), FlowGraph::kEffect); |
| 890 call->deopt_id()), | |
| 891 call->env(), | |
| 892 FlowGraph::kEffect); | |
| 893 } | 835 } |
| 894 | 836 |
| 895 BinaryDoubleOpInstr* double_bin_op = | 837 BinaryDoubleOpInstr* double_bin_op = new (Z) |
| 896 new(Z) BinaryDoubleOpInstr(op_kind, | 838 BinaryDoubleOpInstr(op_kind, new (Z) Value(left), new (Z) Value(right), |
| 897 new(Z) Value(left), | 839 call->deopt_id(), call->token_pos()); |
| 898 new(Z) Value(right), | |
| 899 call->deopt_id(), call->token_pos()); | |
| 900 ReplaceCall(call, double_bin_op); | 840 ReplaceCall(call, double_bin_op); |
| 901 } else if (operands_type == kMintCid) { | 841 } else if (operands_type == kMintCid) { |
| 902 if (!FlowGraphCompiler::SupportsUnboxedMints()) return false; | 842 if (!FlowGraphCompiler::SupportsUnboxedMints()) return false; |
| 903 if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) { | 843 if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) { |
| 904 ShiftMintOpInstr* shift_op = | 844 ShiftMintOpInstr* shift_op = new (Z) ShiftMintOpInstr( |
| 905 new(Z) ShiftMintOpInstr( | 845 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 906 op_kind, new(Z) Value(left), new(Z) Value(right), | |
| 907 call->deopt_id()); | |
| 908 ReplaceCall(call, shift_op); | 846 ReplaceCall(call, shift_op); |
| 909 } else { | 847 } else { |
| 910 BinaryMintOpInstr* bin_op = | 848 BinaryMintOpInstr* bin_op = new (Z) BinaryMintOpInstr( |
| 911 new(Z) BinaryMintOpInstr( | 849 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 912 op_kind, new(Z) Value(left), new(Z) Value(right), | |
| 913 call->deopt_id()); | |
| 914 ReplaceCall(call, bin_op); | 850 ReplaceCall(call, bin_op); |
| 915 } | 851 } |
| 916 } else if (operands_type == kFloat32x4Cid) { | 852 } else if (operands_type == kFloat32x4Cid) { |
| 917 return InlineFloat32x4BinaryOp(call, op_kind); | 853 return InlineFloat32x4BinaryOp(call, op_kind); |
| 918 } else if (operands_type == kInt32x4Cid) { | 854 } else if (operands_type == kInt32x4Cid) { |
| 919 return InlineInt32x4BinaryOp(call, op_kind); | 855 return InlineInt32x4BinaryOp(call, op_kind); |
| 920 } else if (operands_type == kFloat64x2Cid) { | 856 } else if (operands_type == kFloat64x2Cid) { |
| 921 return InlineFloat64x2BinaryOp(call, op_kind); | 857 return InlineFloat64x2BinaryOp(call, op_kind); |
| 922 } else if (op_kind == Token::kMOD) { | 858 } else if (op_kind == Token::kMOD) { |
| 923 ASSERT(operands_type == kSmiCid); | 859 ASSERT(operands_type == kSmiCid); |
| 924 if (right->IsConstant()) { | 860 if (right->IsConstant()) { |
| 925 const Object& obj = right->AsConstant()->value(); | 861 const Object& obj = right->AsConstant()->value(); |
| 926 if (obj.IsSmi() && Utils::IsPowerOfTwo(Smi::Cast(obj).Value())) { | 862 if (obj.IsSmi() && Utils::IsPowerOfTwo(Smi::Cast(obj).Value())) { |
| 927 // Insert smi check and attach a copy of the original environment | 863 // Insert smi check and attach a copy of the original environment |
| 928 // because the smi operation can still deoptimize. | 864 // because the smi operation can still deoptimize. |
| 929 InsertBefore(call, | 865 InsertBefore(call, |
| 930 new(Z) CheckSmiInstr(new(Z) Value(left), | 866 new (Z) CheckSmiInstr(new (Z) Value(left), |
| 931 call->deopt_id(), | 867 call->deopt_id(), call->token_pos()), |
| 932 call->token_pos()), | 868 call->env(), FlowGraph::kEffect); |
| 933 call->env(), | 869 ConstantInstr* constant = flow_graph()->GetConstant( |
| 934 FlowGraph::kEffect); | 870 Smi::Handle(Z, Smi::New(Smi::Cast(obj).Value() - 1))); |
| 935 ConstantInstr* constant = | |
| 936 flow_graph()->GetConstant(Smi::Handle(Z, | |
| 937 Smi::New(Smi::Cast(obj).Value() - 1))); | |
| 938 BinarySmiOpInstr* bin_op = | 871 BinarySmiOpInstr* bin_op = |
| 939 new(Z) BinarySmiOpInstr(Token::kBIT_AND, | 872 new (Z) BinarySmiOpInstr(Token::kBIT_AND, new (Z) Value(left), |
| 940 new(Z) Value(left), | 873 new (Z) Value(constant), call->deopt_id()); |
| 941 new(Z) Value(constant), | |
| 942 call->deopt_id()); | |
| 943 ReplaceCall(call, bin_op); | 874 ReplaceCall(call, bin_op); |
| 944 return true; | 875 return true; |
| 945 } | 876 } |
| 946 } | 877 } |
| 947 // Insert two smi checks and attach a copy of the original | 878 // Insert two smi checks and attach a copy of the original |
| 948 // environment because the smi operation can still deoptimize. | 879 // environment because the smi operation can still deoptimize. |
| 949 AddCheckSmi(left, call->deopt_id(), call->env(), call); | 880 AddCheckSmi(left, call->deopt_id(), call->env(), call); |
| 950 AddCheckSmi(right, call->deopt_id(), call->env(), call); | 881 AddCheckSmi(right, call->deopt_id(), call->env(), call); |
| 951 BinarySmiOpInstr* bin_op = | 882 BinarySmiOpInstr* bin_op = new (Z) BinarySmiOpInstr( |
| 952 new(Z) BinarySmiOpInstr(op_kind, | 883 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 953 new(Z) Value(left), | |
| 954 new(Z) Value(right), | |
| 955 call->deopt_id()); | |
| 956 ReplaceCall(call, bin_op); | 884 ReplaceCall(call, bin_op); |
| 957 } else { | 885 } else { |
| 958 ASSERT(operands_type == kSmiCid); | 886 ASSERT(operands_type == kSmiCid); |
| 959 // Insert two smi checks and attach a copy of the original | 887 // Insert two smi checks and attach a copy of the original |
| 960 // environment because the smi operation can still deoptimize. | 888 // environment because the smi operation can still deoptimize. |
| 961 AddCheckSmi(left, call->deopt_id(), call->env(), call); | 889 AddCheckSmi(left, call->deopt_id(), call->env(), call); |
| 962 AddCheckSmi(right, call->deopt_id(), call->env(), call); | 890 AddCheckSmi(right, call->deopt_id(), call->env(), call); |
| 963 if (left->IsConstant() && | 891 if (left->IsConstant() && |
| 964 ((op_kind == Token::kADD) || (op_kind == Token::kMUL))) { | 892 ((op_kind == Token::kADD) || (op_kind == Token::kMUL))) { |
| 965 // Constant should be on the right side. | 893 // Constant should be on the right side. |
| 966 Definition* temp = left; | 894 Definition* temp = left; |
| 967 left = right; | 895 left = right; |
| 968 right = temp; | 896 right = temp; |
| 969 } | 897 } |
| 970 BinarySmiOpInstr* bin_op = | 898 BinarySmiOpInstr* bin_op = new (Z) BinarySmiOpInstr( |
| 971 new(Z) BinarySmiOpInstr( | 899 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 972 op_kind, | |
| 973 new(Z) Value(left), | |
| 974 new(Z) Value(right), | |
| 975 call->deopt_id()); | |
| 976 ReplaceCall(call, bin_op); | 900 ReplaceCall(call, bin_op); |
| 977 } | 901 } |
| 978 return true; | 902 return true; |
| 979 } | 903 } |
| 980 | 904 |
| 981 | 905 |
| 982 bool JitOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, | 906 bool JitOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, |
| 983 Token::Kind op_kind) { | 907 Token::Kind op_kind) { |
| 984 ASSERT(call->ArgumentCount() == 1); | 908 ASSERT(call->ArgumentCount() == 1); |
| 985 Definition* input = call->ArgumentAt(0); | 909 Definition* input = call->ArgumentAt(0); |
| 986 Definition* unary_op = NULL; | 910 Definition* unary_op = NULL; |
| 987 if (HasOnlyOneSmi(*call->ic_data())) { | 911 if (HasOnlyOneSmi(*call->ic_data())) { |
| 988 InsertBefore(call, | 912 InsertBefore(call, |
| 989 new(Z) CheckSmiInstr(new(Z) Value(input), | 913 new (Z) CheckSmiInstr(new (Z) Value(input), call->deopt_id(), |
| 990 call->deopt_id(), | 914 call->token_pos()), |
| 991 call->token_pos()), | 915 call->env(), FlowGraph::kEffect); |
| 992 call->env(), | 916 unary_op = new (Z) |
| 993 FlowGraph::kEffect); | 917 UnarySmiOpInstr(op_kind, new (Z) Value(input), call->deopt_id()); |
| 994 unary_op = new(Z) UnarySmiOpInstr( | |
| 995 op_kind, new(Z) Value(input), call->deopt_id()); | |
| 996 } else if ((op_kind == Token::kBIT_NOT) && | 918 } else if ((op_kind == Token::kBIT_NOT) && |
| 997 HasOnlySmiOrMint(*call->ic_data()) && | 919 HasOnlySmiOrMint(*call->ic_data()) && |
| 998 FlowGraphCompiler::SupportsUnboxedMints()) { | 920 FlowGraphCompiler::SupportsUnboxedMints()) { |
| 999 unary_op = new(Z) UnaryMintOpInstr( | 921 unary_op = new (Z) |
| 1000 op_kind, new(Z) Value(input), call->deopt_id()); | 922 UnaryMintOpInstr(op_kind, new (Z) Value(input), call->deopt_id()); |
| 1001 } else if (HasOnlyOneDouble(*call->ic_data()) && | 923 } else if (HasOnlyOneDouble(*call->ic_data()) && |
| 1002 (op_kind == Token::kNEGATE) && | 924 (op_kind == Token::kNEGATE) && CanUnboxDouble()) { |
| 1003 CanUnboxDouble()) { | |
| 1004 AddReceiverCheck(call); | 925 AddReceiverCheck(call); |
| 1005 unary_op = new(Z) UnaryDoubleOpInstr( | 926 unary_op = new (Z) UnaryDoubleOpInstr(Token::kNEGATE, new (Z) Value(input), |
| 1006 Token::kNEGATE, new(Z) Value(input), call->deopt_id()); | 927 call->deopt_id()); |
| 1007 } else { | 928 } else { |
| 1008 return false; | 929 return false; |
| 1009 } | 930 } |
| 1010 ASSERT(unary_op != NULL); | 931 ASSERT(unary_op != NULL); |
| 1011 ReplaceCall(call, unary_op); | 932 ReplaceCall(call, unary_op); |
| 1012 return true; | 933 return true; |
| 1013 } | 934 } |
| 1014 | 935 |
| 1015 | 936 |
| 1016 // Using field class. | 937 // Using field class. |
| 1017 RawField* JitOptimizer::GetField(intptr_t class_id, | 938 RawField* JitOptimizer::GetField(intptr_t class_id, const String& field_name) { |
| 1018 const String& field_name) { | |
| 1019 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); | 939 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); |
| 1020 Field& field = Field::Handle(Z); | 940 Field& field = Field::Handle(Z); |
| 1021 while (!cls.IsNull()) { | 941 while (!cls.IsNull()) { |
| 1022 field = cls.LookupInstanceField(field_name); | 942 field = cls.LookupInstanceField(field_name); |
| 1023 if (!field.IsNull()) { | 943 if (!field.IsNull()) { |
| 1024 if (Compiler::IsBackgroundCompilation() || | 944 if (Compiler::IsBackgroundCompilation() || |
| 1025 FLAG_force_clone_compiler_objects) { | 945 FLAG_force_clone_compiler_objects) { |
| 1026 return field.CloneFromOriginal(); | 946 return field.CloneFromOriginal(); |
| 1027 } else { | 947 } else { |
| 1028 return field.raw(); | 948 return field.raw(); |
| 1029 } | 949 } |
| 1030 } | 950 } |
| 1031 cls = cls.SuperClass(); | 951 cls = cls.SuperClass(); |
| 1032 } | 952 } |
| 1033 return Field::null(); | 953 return Field::null(); |
| 1034 } | 954 } |
| 1035 | 955 |
| 1036 | 956 |
| 1037 bool JitOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call) { | 957 bool JitOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call) { |
| 1038 ASSERT(call->HasICData()); | 958 ASSERT(call->HasICData()); |
| 1039 const ICData& ic_data = *call->ic_data(); | 959 const ICData& ic_data = *call->ic_data(); |
| 1040 ASSERT(ic_data.HasOneTarget()); | 960 ASSERT(ic_data.HasOneTarget()); |
| 1041 GrowableArray<intptr_t> class_ids; | 961 GrowableArray<intptr_t> class_ids; |
| 1042 ic_data.GetClassIdsAt(0, &class_ids); | 962 ic_data.GetClassIdsAt(0, &class_ids); |
| 1043 ASSERT(class_ids.length() == 1); | 963 ASSERT(class_ids.length() == 1); |
| 1044 // Inline implicit instance getter. | 964 // Inline implicit instance getter. |
| 1045 const String& field_name = | 965 const String& field_name = |
| 1046 String::Handle(Z, Field::NameFromGetter(call->function_name())); | 966 String::Handle(Z, Field::NameFromGetter(call->function_name())); |
| 1047 const Field& field = | 967 const Field& field = Field::ZoneHandle(Z, GetField(class_ids[0], field_name)); |
| 1048 Field::ZoneHandle(Z, GetField(class_ids[0], field_name)); | |
| 1049 ASSERT(!field.IsNull()); | 968 ASSERT(!field.IsNull()); |
| 1050 | 969 |
| 1051 if (flow_graph()->InstanceCallNeedsClassCheck( | 970 if (flow_graph()->InstanceCallNeedsClassCheck(call, |
| 1052 call, RawFunction::kImplicitGetter)) { | 971 RawFunction::kImplicitGetter)) { |
| 1053 AddReceiverCheck(call); | 972 AddReceiverCheck(call); |
| 1054 } | 973 } |
| 1055 LoadFieldInstr* load = new(Z) LoadFieldInstr( | 974 LoadFieldInstr* load = new (Z) LoadFieldInstr( |
| 1056 new(Z) Value(call->ArgumentAt(0)), | 975 new (Z) Value(call->ArgumentAt(0)), &field, |
| 1057 &field, | 976 AbstractType::ZoneHandle(Z, field.type()), call->token_pos()); |
| 1058 AbstractType::ZoneHandle(Z, field.type()), | |
| 1059 call->token_pos()); | |
| 1060 load->set_is_immutable(field.is_final()); | 977 load->set_is_immutable(field.is_final()); |
| 1061 if (field.guarded_cid() != kIllegalCid) { | 978 if (field.guarded_cid() != kIllegalCid) { |
| 1062 if (!field.is_nullable() || (field.guarded_cid() == kNullCid)) { | 979 if (!field.is_nullable() || (field.guarded_cid() == kNullCid)) { |
| 1063 load->set_result_cid(field.guarded_cid()); | 980 load->set_result_cid(field.guarded_cid()); |
| 1064 } | 981 } |
| 1065 flow_graph()->parsed_function().AddToGuardedFields(&field); | 982 flow_graph()->parsed_function().AddToGuardedFields(&field); |
| 1066 } | 983 } |
| 1067 | 984 |
| 1068 // Discard the environment from the original instruction because the load | 985 // Discard the environment from the original instruction because the load |
| 1069 // can't deoptimize. | 986 // can't deoptimize. |
| 1070 call->RemoveEnvironment(); | 987 call->RemoveEnvironment(); |
| 1071 ReplaceCall(call, load); | 988 ReplaceCall(call, load); |
| 1072 | 989 |
| 1073 if (load->result_cid() != kDynamicCid) { | 990 if (load->result_cid() != kDynamicCid) { |
| 1074 // Reset value types if guarded_cid was used. | 991 // Reset value types if guarded_cid was used. |
| 1075 for (Value::Iterator it(load->input_use_list()); | 992 for (Value::Iterator it(load->input_use_list()); !it.Done(); it.Advance()) { |
| 1076 !it.Done(); | |
| 1077 it.Advance()) { | |
| 1078 it.Current()->SetReachingType(NULL); | 993 it.Current()->SetReachingType(NULL); |
| 1079 } | 994 } |
| 1080 } | 995 } |
| 1081 return true; | 996 return true; |
| 1082 } | 997 } |
| 1083 | 998 |
| 1084 | 999 |
| 1085 bool JitOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, | 1000 bool JitOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, |
| 1086 Token::Kind op_kind) { | 1001 Token::Kind op_kind) { |
| 1087 if (!ShouldInlineSimd()) { | 1002 if (!ShouldInlineSimd()) { |
| 1088 return false; | 1003 return false; |
| 1089 } | 1004 } |
| 1090 ASSERT(call->ArgumentCount() == 2); | 1005 ASSERT(call->ArgumentCount() == 2); |
| 1091 Definition* left = call->ArgumentAt(0); | 1006 Definition* left = call->ArgumentAt(0); |
| 1092 Definition* right = call->ArgumentAt(1); | 1007 Definition* right = call->ArgumentAt(1); |
| 1093 // Type check left. | 1008 // Type check left. |
| 1094 AddCheckClass(left, | 1009 AddCheckClass(left, ICData::ZoneHandle( |
| 1095 ICData::ZoneHandle( | 1010 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1096 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1011 call->deopt_id(), call->env(), call); |
| 1097 call->deopt_id(), | |
| 1098 call->env(), | |
| 1099 call); | |
| 1100 // Type check right. | 1012 // Type check right. |
| 1101 AddCheckClass(right, | 1013 AddCheckClass(right, ICData::ZoneHandle( |
| 1102 ICData::ZoneHandle( | 1014 Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)), |
| 1103 Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)), | 1015 call->deopt_id(), call->env(), call); |
| 1104 call->deopt_id(), | |
| 1105 call->env(), | |
| 1106 call); | |
| 1107 // Replace call. | 1016 // Replace call. |
| 1108 BinaryFloat32x4OpInstr* float32x4_bin_op = | 1017 BinaryFloat32x4OpInstr* float32x4_bin_op = new (Z) BinaryFloat32x4OpInstr( |
| 1109 new(Z) BinaryFloat32x4OpInstr( | 1018 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 1110 op_kind, new(Z) Value(left), new(Z) Value(right), | |
| 1111 call->deopt_id()); | |
| 1112 ReplaceCall(call, float32x4_bin_op); | 1019 ReplaceCall(call, float32x4_bin_op); |
| 1113 | 1020 |
| 1114 return true; | 1021 return true; |
| 1115 } | 1022 } |
| 1116 | 1023 |
| 1117 | 1024 |
| 1118 bool JitOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, | 1025 bool JitOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, |
| 1119 Token::Kind op_kind) { | 1026 Token::Kind op_kind) { |
| 1120 if (!ShouldInlineSimd()) { | 1027 if (!ShouldInlineSimd()) { |
| 1121 return false; | 1028 return false; |
| 1122 } | 1029 } |
| 1123 ASSERT(call->ArgumentCount() == 2); | 1030 ASSERT(call->ArgumentCount() == 2); |
| 1124 Definition* left = call->ArgumentAt(0); | 1031 Definition* left = call->ArgumentAt(0); |
| 1125 Definition* right = call->ArgumentAt(1); | 1032 Definition* right = call->ArgumentAt(1); |
| 1126 // Type check left. | 1033 // Type check left. |
| 1127 AddCheckClass(left, | 1034 AddCheckClass(left, ICData::ZoneHandle( |
| 1128 ICData::ZoneHandle( | 1035 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1129 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1036 call->deopt_id(), call->env(), call); |
| 1130 call->deopt_id(), | |
| 1131 call->env(), | |
| 1132 call); | |
| 1133 // Type check right. | 1037 // Type check right. |
| 1134 AddCheckClass(right, | 1038 AddCheckClass(right, ICData::ZoneHandle( |
| 1135 ICData::ZoneHandle(Z, | 1039 Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)), |
| 1136 call->ic_data()->AsUnaryClassChecksForArgNr(1)), | 1040 call->deopt_id(), call->env(), call); |
| 1137 call->deopt_id(), | |
| 1138 call->env(), | |
| 1139 call); | |
| 1140 // Replace call. | 1041 // Replace call. |
| 1141 BinaryInt32x4OpInstr* int32x4_bin_op = | 1042 BinaryInt32x4OpInstr* int32x4_bin_op = new (Z) BinaryInt32x4OpInstr( |
| 1142 new(Z) BinaryInt32x4OpInstr( | 1043 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 1143 op_kind, new(Z) Value(left), new(Z) Value(right), | |
| 1144 call->deopt_id()); | |
| 1145 ReplaceCall(call, int32x4_bin_op); | 1044 ReplaceCall(call, int32x4_bin_op); |
| 1146 return true; | 1045 return true; |
| 1147 } | 1046 } |
| 1148 | 1047 |
| 1149 | 1048 |
| 1150 bool JitOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, | 1049 bool JitOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, |
| 1151 Token::Kind op_kind) { | 1050 Token::Kind op_kind) { |
| 1152 if (!ShouldInlineSimd()) { | 1051 if (!ShouldInlineSimd()) { |
| 1153 return false; | 1052 return false; |
| 1154 } | 1053 } |
| 1155 ASSERT(call->ArgumentCount() == 2); | 1054 ASSERT(call->ArgumentCount() == 2); |
| 1156 Definition* left = call->ArgumentAt(0); | 1055 Definition* left = call->ArgumentAt(0); |
| 1157 Definition* right = call->ArgumentAt(1); | 1056 Definition* right = call->ArgumentAt(1); |
| 1158 // Type check left. | 1057 // Type check left. |
| 1159 AddCheckClass(left, | 1058 AddCheckClass( |
| 1160 ICData::ZoneHandle( | 1059 left, ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1161 call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1060 call->deopt_id(), call->env(), call); |
| 1162 call->deopt_id(), | |
| 1163 call->env(), | |
| 1164 call); | |
| 1165 // Type check right. | 1061 // Type check right. |
| 1166 AddCheckClass(right, | 1062 AddCheckClass( |
| 1167 ICData::ZoneHandle( | 1063 right, ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecksForArgNr(1)), |
| 1168 call->ic_data()->AsUnaryClassChecksForArgNr(1)), | 1064 call->deopt_id(), call->env(), call); |
| 1169 call->deopt_id(), | |
| 1170 call->env(), | |
| 1171 call); | |
| 1172 // Replace call. | 1065 // Replace call. |
| 1173 BinaryFloat64x2OpInstr* float64x2_bin_op = | 1066 BinaryFloat64x2OpInstr* float64x2_bin_op = new (Z) BinaryFloat64x2OpInstr( |
| 1174 new(Z) BinaryFloat64x2OpInstr( | 1067 op_kind, new (Z) Value(left), new (Z) Value(right), call->deopt_id()); |
| 1175 op_kind, new(Z) Value(left), new(Z) Value(right), | |
| 1176 call->deopt_id()); | |
| 1177 ReplaceCall(call, float64x2_bin_op); | 1068 ReplaceCall(call, float64x2_bin_op); |
| 1178 return true; | 1069 return true; |
| 1179 } | 1070 } |
| 1180 | 1071 |
| 1181 | 1072 |
| 1182 // Only unique implicit instance getters can be currently handled. | 1073 // Only unique implicit instance getters can be currently handled. |
| 1183 bool JitOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { | 1074 bool JitOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { |
| 1184 ASSERT(call->HasICData()); | 1075 ASSERT(call->HasICData()); |
| 1185 const ICData& ic_data = *call->ic_data(); | 1076 const ICData& ic_data = *call->ic_data(); |
| 1186 if (ic_data.NumberOfUsedChecks() == 0) { | 1077 if (ic_data.NumberOfUsedChecks() == 0) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1202 } | 1093 } |
| 1203 return InlineImplicitInstanceGetter(call); | 1094 return InlineImplicitInstanceGetter(call); |
| 1204 } | 1095 } |
| 1205 | 1096 |
| 1206 | 1097 |
| 1207 void JitOptimizer::ReplaceWithMathCFunction( | 1098 void JitOptimizer::ReplaceWithMathCFunction( |
| 1208 InstanceCallInstr* call, | 1099 InstanceCallInstr* call, |
| 1209 MethodRecognizer::Kind recognized_kind) { | 1100 MethodRecognizer::Kind recognized_kind) { |
| 1210 AddReceiverCheck(call); | 1101 AddReceiverCheck(call); |
| 1211 ZoneGrowableArray<Value*>* args = | 1102 ZoneGrowableArray<Value*>* args = |
| 1212 new(Z) ZoneGrowableArray<Value*>(call->ArgumentCount()); | 1103 new (Z) ZoneGrowableArray<Value*>(call->ArgumentCount()); |
| 1213 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { | 1104 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { |
| 1214 args->Add(new(Z) Value(call->ArgumentAt(i))); | 1105 args->Add(new (Z) Value(call->ArgumentAt(i))); |
| 1215 } | 1106 } |
| 1216 InvokeMathCFunctionInstr* invoke = | 1107 InvokeMathCFunctionInstr* invoke = new (Z) InvokeMathCFunctionInstr( |
| 1217 new(Z) InvokeMathCFunctionInstr(args, | 1108 args, call->deopt_id(), recognized_kind, call->token_pos()); |
| 1218 call->deopt_id(), | |
| 1219 recognized_kind, | |
| 1220 call->token_pos()); | |
| 1221 ReplaceCall(call, invoke); | 1109 ReplaceCall(call, invoke); |
| 1222 } | 1110 } |
| 1223 | 1111 |
| 1224 | 1112 |
| 1225 // Inline only simple, frequently called core library methods. | 1113 // Inline only simple, frequently called core library methods. |
| 1226 bool JitOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { | 1114 bool JitOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { |
| 1227 ASSERT(call->HasICData()); | 1115 ASSERT(call->HasICData()); |
| 1228 const ICData& ic_data = *call->ic_data(); | 1116 const ICData& ic_data = *call->ic_data(); |
| 1229 if (ic_data.NumberOfUsedChecks() != 1) { | 1117 if (ic_data.NumberOfUsedChecks() != 1) { |
| 1230 // No type feedback collected or multiple targets found. | 1118 // No type feedback collected or multiple targets found. |
| 1231 return false; | 1119 return false; |
| 1232 } | 1120 } |
| 1233 | 1121 |
| 1234 Function& target = Function::Handle(Z); | 1122 Function& target = Function::Handle(Z); |
| 1235 GrowableArray<intptr_t> class_ids; | 1123 GrowableArray<intptr_t> class_ids; |
| 1236 ic_data.GetCheckAt(0, &class_ids, &target); | 1124 ic_data.GetCheckAt(0, &class_ids, &target); |
| 1237 MethodRecognizer::Kind recognized_kind = | 1125 MethodRecognizer::Kind recognized_kind = |
| 1238 MethodRecognizer::RecognizeKind(target); | 1126 MethodRecognizer::RecognizeKind(target); |
| 1239 | 1127 |
| 1240 if (CanUnboxDouble() && | 1128 if (CanUnboxDouble() && |
| 1241 (recognized_kind == MethodRecognizer::kIntegerToDouble)) { | 1129 (recognized_kind == MethodRecognizer::kIntegerToDouble)) { |
| 1242 if (class_ids[0] == kSmiCid) { | 1130 if (class_ids[0] == kSmiCid) { |
| 1243 AddReceiverCheck(call); | 1131 AddReceiverCheck(call); |
| 1244 ReplaceCall(call, | 1132 ReplaceCall(call, |
| 1245 new(Z) SmiToDoubleInstr( | 1133 new (Z) SmiToDoubleInstr(new (Z) Value(call->ArgumentAt(0)), |
| 1246 new(Z) Value(call->ArgumentAt(0)), | 1134 call->token_pos())); |
| 1247 call->token_pos())); | |
| 1248 return true; | 1135 return true; |
| 1249 } else if ((class_ids[0] == kMintCid) && CanConvertUnboxedMintToDouble()) { | 1136 } else if ((class_ids[0] == kMintCid) && CanConvertUnboxedMintToDouble()) { |
| 1250 AddReceiverCheck(call); | 1137 AddReceiverCheck(call); |
| 1251 ReplaceCall(call, | 1138 ReplaceCall(call, |
| 1252 new(Z) MintToDoubleInstr(new(Z) Value(call->ArgumentAt(0)), | 1139 new (Z) MintToDoubleInstr(new (Z) Value(call->ArgumentAt(0)), |
| 1253 call->deopt_id())); | 1140 call->deopt_id())); |
| 1254 return true; | 1141 return true; |
| 1255 } | 1142 } |
| 1256 } | 1143 } |
| 1257 | 1144 |
| 1258 if (class_ids[0] == kDoubleCid) { | 1145 if (class_ids[0] == kDoubleCid) { |
| 1259 if (!CanUnboxDouble()) { | 1146 if (!CanUnboxDouble()) { |
| 1260 return false; | 1147 return false; |
| 1261 } | 1148 } |
| 1262 switch (recognized_kind) { | 1149 switch (recognized_kind) { |
| 1263 case MethodRecognizer::kDoubleToInteger: { | 1150 case MethodRecognizer::kDoubleToInteger: { |
| 1264 AddReceiverCheck(call); | 1151 AddReceiverCheck(call); |
| 1265 ASSERT(call->HasICData()); | 1152 ASSERT(call->HasICData()); |
| 1266 const ICData& ic_data = *call->ic_data(); | 1153 const ICData& ic_data = *call->ic_data(); |
| 1267 Definition* input = call->ArgumentAt(0); | 1154 Definition* input = call->ArgumentAt(0); |
| 1268 Definition* d2i_instr = NULL; | 1155 Definition* d2i_instr = NULL; |
| 1269 if (ic_data.HasDeoptReason(ICData::kDeoptDoubleToSmi)) { | 1156 if (ic_data.HasDeoptReason(ICData::kDeoptDoubleToSmi)) { |
| 1270 // Do not repeatedly deoptimize because result didn't fit into Smi. | 1157 // Do not repeatedly deoptimize because result didn't fit into Smi. |
| 1271 d2i_instr = new(Z) DoubleToIntegerInstr( | 1158 d2i_instr = new (Z) DoubleToIntegerInstr(new (Z) Value(input), call); |
| 1272 new(Z) Value(input), call); | |
| 1273 } else { | 1159 } else { |
| 1274 // Optimistically assume result fits into Smi. | 1160 // Optimistically assume result fits into Smi. |
| 1275 d2i_instr = new(Z) DoubleToSmiInstr( | 1161 d2i_instr = |
| 1276 new(Z) Value(input), call->deopt_id()); | 1162 new (Z) DoubleToSmiInstr(new (Z) Value(input), call->deopt_id()); |
| 1277 } | 1163 } |
| 1278 ReplaceCall(call, d2i_instr); | 1164 ReplaceCall(call, d2i_instr); |
| 1279 return true; | 1165 return true; |
| 1280 } | 1166 } |
| 1281 case MethodRecognizer::kDoubleMod: | 1167 case MethodRecognizer::kDoubleMod: |
| 1282 case MethodRecognizer::kDoubleRound: | 1168 case MethodRecognizer::kDoubleRound: |
| 1283 ReplaceWithMathCFunction(call, recognized_kind); | 1169 ReplaceWithMathCFunction(call, recognized_kind); |
| 1284 return true; | 1170 return true; |
| 1285 case MethodRecognizer::kDoubleTruncate: | 1171 case MethodRecognizer::kDoubleTruncate: |
| 1286 case MethodRecognizer::kDoubleFloor: | 1172 case MethodRecognizer::kDoubleFloor: |
| 1287 case MethodRecognizer::kDoubleCeil: | 1173 case MethodRecognizer::kDoubleCeil: |
| 1288 if (!TargetCPUFeatures::double_truncate_round_supported()) { | 1174 if (!TargetCPUFeatures::double_truncate_round_supported()) { |
| 1289 ReplaceWithMathCFunction(call, recognized_kind); | 1175 ReplaceWithMathCFunction(call, recognized_kind); |
| 1290 } else { | 1176 } else { |
| 1291 AddReceiverCheck(call); | 1177 AddReceiverCheck(call); |
| 1292 DoubleToDoubleInstr* d2d_instr = | 1178 DoubleToDoubleInstr* d2d_instr = |
| 1293 new(Z) DoubleToDoubleInstr(new(Z) Value(call->ArgumentAt(0)), | 1179 new (Z) DoubleToDoubleInstr(new (Z) Value(call->ArgumentAt(0)), |
| 1294 recognized_kind, call->deopt_id()); | 1180 recognized_kind, call->deopt_id()); |
| 1295 ReplaceCall(call, d2d_instr); | 1181 ReplaceCall(call, d2d_instr); |
| 1296 } | 1182 } |
| 1297 return true; | 1183 return true; |
| 1298 default: | 1184 default: |
| 1299 break; | 1185 break; |
| 1300 } | 1186 } |
| 1301 } | 1187 } |
| 1302 | 1188 |
| 1303 return FlowGraphInliner::TryReplaceInstanceCallWithInline( | 1189 return FlowGraphInliner::TryReplaceInstanceCallWithInline( |
| 1304 flow_graph_, current_iterator(), call); | 1190 flow_graph_, current_iterator(), call); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1324 const Class& type_class = Class::Handle(Z, type.type_class()); | 1210 const Class& type_class = Class::Handle(Z, type.type_class()); |
| 1325 const intptr_t num_type_args = type_class.NumTypeArguments(); | 1211 const intptr_t num_type_args = type_class.NumTypeArguments(); |
| 1326 if (num_type_args > 0) { | 1212 if (num_type_args > 0) { |
| 1327 // Only raw types can be directly compared, thus disregarding type | 1213 // Only raw types can be directly compared, thus disregarding type |
| 1328 // arguments. | 1214 // arguments. |
| 1329 const intptr_t num_type_params = type_class.NumTypeParameters(); | 1215 const intptr_t num_type_params = type_class.NumTypeParameters(); |
| 1330 const intptr_t from_index = num_type_args - num_type_params; | 1216 const intptr_t from_index = num_type_args - num_type_params; |
| 1331 const TypeArguments& type_arguments = | 1217 const TypeArguments& type_arguments = |
| 1332 TypeArguments::Handle(Z, type.arguments()); | 1218 TypeArguments::Handle(Z, type.arguments()); |
| 1333 const bool is_raw_type = type_arguments.IsNull() || | 1219 const bool is_raw_type = type_arguments.IsNull() || |
| 1334 type_arguments.IsRaw(from_index, num_type_params); | 1220 type_arguments.IsRaw(from_index, num_type_params); |
| 1335 if (!is_raw_type) { | 1221 if (!is_raw_type) { |
| 1336 // Unknown result. | 1222 // Unknown result. |
| 1337 return Bool::null(); | 1223 return Bool::null(); |
| 1338 } | 1224 } |
| 1339 } | 1225 } |
| 1340 | 1226 |
| 1341 const ClassTable& class_table = *isolate()->class_table(); | 1227 const ClassTable& class_table = *isolate()->class_table(); |
| 1342 Bool& prev = Bool::Handle(Z); | 1228 Bool& prev = Bool::Handle(Z); |
| 1343 Class& cls = Class::Handle(Z); | 1229 Class& cls = Class::Handle(Z); |
| 1344 | 1230 |
| 1345 bool results_differ = false; | 1231 bool results_differ = false; |
| 1346 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { | 1232 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { |
| 1347 cls = class_table.At(ic_data.GetReceiverClassIdAt(i)); | 1233 cls = class_table.At(ic_data.GetReceiverClassIdAt(i)); |
| 1348 if (cls.NumTypeArguments() > 0) { | 1234 if (cls.NumTypeArguments() > 0) { |
| 1349 return Bool::null(); | 1235 return Bool::null(); |
| 1350 } | 1236 } |
| 1351 const bool is_subtype = cls.IsSubtypeOf( | 1237 const bool is_subtype = |
| 1352 TypeArguments::Handle(Z), | 1238 cls.IsSubtypeOf(TypeArguments::Handle(Z), type_class, |
| 1353 type_class, | 1239 TypeArguments::Handle(Z), NULL, NULL, Heap::kOld); |
| 1354 TypeArguments::Handle(Z), | |
| 1355 NULL, | |
| 1356 NULL, | |
| 1357 Heap::kOld); | |
| 1358 results->Add(cls.id()); | 1240 results->Add(cls.id()); |
| 1359 results->Add(is_subtype); | 1241 results->Add(is_subtype); |
| 1360 if (prev.IsNull()) { | 1242 if (prev.IsNull()) { |
| 1361 prev = Bool::Get(is_subtype).raw(); | 1243 prev = Bool::Get(is_subtype).raw(); |
| 1362 } else { | 1244 } else { |
| 1363 if (is_subtype != prev.value()) { | 1245 if (is_subtype != prev.value()) { |
| 1364 results_differ = true; | 1246 results_differ = true; |
| 1365 } | 1247 } |
| 1366 } | 1248 } |
| 1367 } | 1249 } |
| 1368 return results_differ ? Bool::null() : prev.raw(); | 1250 return results_differ ? Bool::null() : prev.raw(); |
| 1369 } | 1251 } |
| 1370 | 1252 |
| 1371 | 1253 |
| 1372 // Returns true if checking against this type is a direct class id comparison. | 1254 // Returns true if checking against this type is a direct class id comparison. |
| 1373 bool JitOptimizer::TypeCheckAsClassEquality(const AbstractType& type) { | 1255 bool JitOptimizer::TypeCheckAsClassEquality(const AbstractType& type) { |
| 1374 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); | 1256 ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); |
| 1375 // Requires CHA. | 1257 // Requires CHA. |
| 1376 if (!type.IsInstantiated()) return false; | 1258 if (!type.IsInstantiated()) return false; |
| 1377 // Function types have different type checking rules. | 1259 // Function types have different type checking rules. |
| 1378 if (type.IsFunctionType()) return false; | 1260 if (type.IsFunctionType()) return false; |
| 1379 const Class& type_class = Class::Handle(type.type_class()); | 1261 const Class& type_class = Class::Handle(type.type_class()); |
| 1380 // Could be an interface check? | 1262 // Could be an interface check? |
| 1381 if (CHA::IsImplemented(type_class)) return false; | 1263 if (CHA::IsImplemented(type_class)) return false; |
| 1382 // Check if there are subclasses. | 1264 // Check if there are subclasses. |
| 1383 if (CHA::HasSubclasses(type_class)) { | 1265 if (CHA::HasSubclasses(type_class)) { |
| 1384 return false; | 1266 return false; |
| 1385 } | 1267 } |
| 1386 | 1268 |
| 1387 // Private classes cannot be subclassed by later loaded libs. | 1269 // Private classes cannot be subclassed by later loaded libs. |
| 1388 if (!type_class.IsPrivate()) { | 1270 if (!type_class.IsPrivate()) { |
| 1389 if (FLAG_use_cha_deopt || isolate()->all_classes_finalized()) { | 1271 if (FLAG_use_cha_deopt || isolate()->all_classes_finalized()) { |
| 1390 if (FLAG_trace_cha) { | 1272 if (FLAG_trace_cha) { |
| 1391 THR_Print(" **(CHA) Typecheck as class equality since no " | 1273 THR_Print( |
| 1274 " **(CHA) Typecheck as class equality since no " |
| 1392 "subclasses: %s\n", | 1275 "subclasses: %s\n", |
| 1393 type_class.ToCString()); | 1276 type_class.ToCString()); |
| 1394 } | 1277 } |
| 1395 if (FLAG_use_cha_deopt) { | 1278 if (FLAG_use_cha_deopt) { |
| 1396 thread()->cha()->AddToGuardedClasses(type_class, /*subclass_count=*/0); | 1279 thread()->cha()->AddToGuardedClasses(type_class, /*subclass_count=*/0); |
| 1397 } | 1280 } |
| 1398 } else { | 1281 } else { |
| 1399 return false; | 1282 return false; |
| 1400 } | 1283 } |
| 1401 } | 1284 } |
| 1402 const intptr_t num_type_args = type_class.NumTypeArguments(); | 1285 const intptr_t num_type_args = type_class.NumTypeArguments(); |
| 1403 if (num_type_args > 0) { | 1286 if (num_type_args > 0) { |
| 1404 // Only raw types can be directly compared, thus disregarding type | 1287 // Only raw types can be directly compared, thus disregarding type |
| 1405 // arguments. | 1288 // arguments. |
| 1406 const intptr_t num_type_params = type_class.NumTypeParameters(); | 1289 const intptr_t num_type_params = type_class.NumTypeParameters(); |
| 1407 const intptr_t from_index = num_type_args - num_type_params; | 1290 const intptr_t from_index = num_type_args - num_type_params; |
| 1408 const TypeArguments& type_arguments = | 1291 const TypeArguments& type_arguments = |
| 1409 TypeArguments::Handle(type.arguments()); | 1292 TypeArguments::Handle(type.arguments()); |
| 1410 const bool is_raw_type = type_arguments.IsNull() || | 1293 const bool is_raw_type = type_arguments.IsNull() || |
| 1411 type_arguments.IsRaw(from_index, num_type_params); | 1294 type_arguments.IsRaw(from_index, num_type_params); |
| 1412 return is_raw_type; | 1295 return is_raw_type; |
| 1413 } | 1296 } |
| 1414 return true; | 1297 return true; |
| 1415 } | 1298 } |
| 1416 | 1299 |
| 1417 | 1300 |
| 1418 static bool CidTestResultsContains(const ZoneGrowableArray<intptr_t>& results, | 1301 static bool CidTestResultsContains(const ZoneGrowableArray<intptr_t>& results, |
| 1419 intptr_t test_cid) { | 1302 intptr_t test_cid) { |
| 1420 for (intptr_t i = 0; i < results.length(); i += 2) { | 1303 for (intptr_t i = 0; i < results.length(); i += 2) { |
| 1421 if (results[i] == test_cid) return true; | 1304 if (results[i] == test_cid) return true; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1437 // Tries to add cid tests to 'results' so that no deoptimization is | 1320 // Tries to add cid tests to 'results' so that no deoptimization is |
| 1438 // necessary. | 1321 // necessary. |
| 1439 // TODO(srdjan): Do also for other than 'int' type. | 1322 // TODO(srdjan): Do also for other than 'int' type. |
| 1440 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results, | 1323 static bool TryExpandTestCidsResult(ZoneGrowableArray<intptr_t>* results, |
| 1441 const AbstractType& type) { | 1324 const AbstractType& type) { |
| 1442 ASSERT(results->length() >= 2); // At least on eentry. | 1325 ASSERT(results->length() >= 2); // At least on eentry. |
| 1443 const ClassTable& class_table = *Isolate::Current()->class_table(); | 1326 const ClassTable& class_table = *Isolate::Current()->class_table(); |
| 1444 if ((*results)[0] != kSmiCid) { | 1327 if ((*results)[0] != kSmiCid) { |
| 1445 const Class& cls = Class::Handle(class_table.At(kSmiCid)); | 1328 const Class& cls = Class::Handle(class_table.At(kSmiCid)); |
| 1446 const Class& type_class = Class::Handle(type.type_class()); | 1329 const Class& type_class = Class::Handle(type.type_class()); |
| 1447 const bool smi_is_subtype = cls.IsSubtypeOf(TypeArguments::Handle(), | 1330 const bool smi_is_subtype = |
| 1448 type_class, | 1331 cls.IsSubtypeOf(TypeArguments::Handle(), type_class, |
| 1449 TypeArguments::Handle(), | 1332 TypeArguments::Handle(), NULL, NULL, Heap::kOld); |
| 1450 NULL, | |
| 1451 NULL, | |
| 1452 Heap::kOld); | |
| 1453 results->Add((*results)[results->length() - 2]); | 1333 results->Add((*results)[results->length() - 2]); |
| 1454 results->Add((*results)[results->length() - 2]); | 1334 results->Add((*results)[results->length() - 2]); |
| 1455 for (intptr_t i = results->length() - 3; i > 1; --i) { | 1335 for (intptr_t i = results->length() - 3; i > 1; --i) { |
| 1456 (*results)[i] = (*results)[i - 2]; | 1336 (*results)[i] = (*results)[i - 2]; |
| 1457 } | 1337 } |
| 1458 (*results)[0] = kSmiCid; | 1338 (*results)[0] = kSmiCid; |
| 1459 (*results)[1] = smi_is_subtype; | 1339 (*results)[1] = smi_is_subtype; |
| 1460 } | 1340 } |
| 1461 | 1341 |
| 1462 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded()); | 1342 ASSERT(type.IsInstantiated() && !type.IsMalformedOrMalbounded()); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1499 type = Type::IntType(); | 1379 type = Type::IntType(); |
| 1500 } else if (matches_core(call, Symbols::_instanceOfSmi())) { | 1380 } else if (matches_core(call, Symbols::_instanceOfSmi())) { |
| 1501 type = Type::SmiType(); | 1381 type = Type::SmiType(); |
| 1502 } else if (matches_core(call, Symbols::_instanceOfDouble())) { | 1382 } else if (matches_core(call, Symbols::_instanceOfDouble())) { |
| 1503 type = Type::Double(); | 1383 type = Type::Double(); |
| 1504 } else if (matches_core(call, Symbols::_instanceOfString())) { | 1384 } else if (matches_core(call, Symbols::_instanceOfString())) { |
| 1505 type = Type::StringType(); | 1385 type = Type::StringType(); |
| 1506 } else { | 1386 } else { |
| 1507 UNIMPLEMENTED(); | 1387 UNIMPLEMENTED(); |
| 1508 } | 1388 } |
| 1509 negate = Bool::Cast(call->ArgumentAt(1)->OriginalDefinition() | 1389 negate = |
| 1510 ->AsConstant()->value()).value(); | 1390 Bool::Cast( |
| 1391 call->ArgumentAt(1)->OriginalDefinition()->AsConstant()->value()) |
| 1392 .value(); |
| 1511 } | 1393 } |
| 1512 } else { | 1394 } else { |
| 1513 type_args = call->ArgumentAt(1); | 1395 type_args = call->ArgumentAt(1); |
| 1514 type = AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()).raw(); | 1396 type = AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()).raw(); |
| 1515 negate = Bool::Cast(call->ArgumentAt(3)->OriginalDefinition() | 1397 negate = |
| 1516 ->AsConstant()->value()).value(); | 1398 Bool::Cast( |
| 1399 call->ArgumentAt(3)->OriginalDefinition()->AsConstant()->value()) |
| 1400 .value(); |
| 1517 } | 1401 } |
| 1518 const ICData& unary_checks = | 1402 const ICData& unary_checks = |
| 1519 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); | 1403 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); |
| 1520 if ((unary_checks.NumberOfChecks() > 0) && | 1404 if ((unary_checks.NumberOfChecks() > 0) && |
| 1521 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { | 1405 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { |
| 1522 ZoneGrowableArray<intptr_t>* results = | 1406 ZoneGrowableArray<intptr_t>* results = |
| 1523 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2); | 1407 new (Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2); |
| 1524 Bool& as_bool = | 1408 Bool& as_bool = |
| 1525 Bool::ZoneHandle(Z, InstanceOfAsBool(unary_checks, type, results)); | 1409 Bool::ZoneHandle(Z, InstanceOfAsBool(unary_checks, type, results)); |
| 1526 if (as_bool.IsNull()) { | 1410 if (as_bool.IsNull()) { |
| 1527 if (results->length() == unary_checks.NumberOfChecks() * 2) { | 1411 if (results->length() == unary_checks.NumberOfChecks() * 2) { |
| 1528 const bool can_deopt = TryExpandTestCidsResult(results, type); | 1412 const bool can_deopt = TryExpandTestCidsResult(results, type); |
| 1529 TestCidsInstr* test_cids = new(Z) TestCidsInstr( | 1413 TestCidsInstr* test_cids = new (Z) TestCidsInstr( |
| 1530 call->token_pos(), | 1414 call->token_pos(), negate ? Token::kISNOT : Token::kIS, |
| 1531 negate ? Token::kISNOT : Token::kIS, | 1415 new (Z) Value(left), *results, |
| 1532 new(Z) Value(left), | |
| 1533 *results, | |
| 1534 can_deopt ? call->deopt_id() : Thread::kNoDeoptId); | 1416 can_deopt ? call->deopt_id() : Thread::kNoDeoptId); |
| 1535 // Remove type. | 1417 // Remove type. |
| 1536 ReplaceCall(call, test_cids); | 1418 ReplaceCall(call, test_cids); |
| 1537 return; | 1419 return; |
| 1538 } | 1420 } |
| 1539 } else { | 1421 } else { |
| 1540 // TODO(srdjan): Use TestCidsInstr also for this case. | 1422 // TODO(srdjan): Use TestCidsInstr also for this case. |
| 1541 // One result only. | 1423 // One result only. |
| 1542 AddReceiverCheck(call); | 1424 AddReceiverCheck(call); |
| 1543 if (negate) { | 1425 if (negate) { |
| 1544 as_bool = Bool::Get(!as_bool.value()).raw(); | 1426 as_bool = Bool::Get(!as_bool.value()).raw(); |
| 1545 } | 1427 } |
| 1546 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool); | 1428 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool); |
| 1547 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 1429 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 1548 PushArgumentInstr* push = call->PushArgumentAt(i); | 1430 PushArgumentInstr* push = call->PushArgumentAt(i); |
| 1549 push->ReplaceUsesWith(push->value()->definition()); | 1431 push->ReplaceUsesWith(push->value()->definition()); |
| 1550 push->RemoveFromGraph(); | 1432 push->RemoveFromGraph(); |
| 1551 } | 1433 } |
| 1552 call->ReplaceUsesWith(bool_const); | 1434 call->ReplaceUsesWith(bool_const); |
| 1553 ASSERT(current_iterator()->Current() == call); | 1435 ASSERT(current_iterator()->Current() == call); |
| 1554 current_iterator()->RemoveCurrentFromGraph(); | 1436 current_iterator()->RemoveCurrentFromGraph(); |
| 1555 return; | 1437 return; |
| 1556 } | 1438 } |
| 1557 } | 1439 } |
| 1558 | 1440 |
| 1559 if (TypeCheckAsClassEquality(type)) { | 1441 if (TypeCheckAsClassEquality(type)) { |
| 1560 LoadClassIdInstr* left_cid = new(Z) LoadClassIdInstr(new(Z) Value(left)); | 1442 LoadClassIdInstr* left_cid = new (Z) LoadClassIdInstr(new (Z) Value(left)); |
| 1561 InsertBefore(call, | 1443 InsertBefore(call, left_cid, NULL, FlowGraph::kValue); |
| 1562 left_cid, | |
| 1563 NULL, | |
| 1564 FlowGraph::kValue); | |
| 1565 const intptr_t type_cid = Class::Handle(Z, type.type_class()).id(); | 1444 const intptr_t type_cid = Class::Handle(Z, type.type_class()).id(); |
| 1566 ConstantInstr* cid = | 1445 ConstantInstr* cid = |
| 1567 flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(type_cid))); | 1446 flow_graph()->GetConstant(Smi::Handle(Z, Smi::New(type_cid))); |
| 1568 | 1447 |
| 1569 StrictCompareInstr* check_cid = | 1448 StrictCompareInstr* check_cid = new (Z) StrictCompareInstr( |
| 1570 new(Z) StrictCompareInstr( | 1449 call->token_pos(), negate ? Token::kNE_STRICT : Token::kEQ_STRICT, |
| 1571 call->token_pos(), | 1450 new (Z) Value(left_cid), new (Z) Value(cid), |
| 1572 negate ? Token::kNE_STRICT : Token::kEQ_STRICT, | 1451 false); // No number check. |
| 1573 new(Z) Value(left_cid), | |
| 1574 new(Z) Value(cid), | |
| 1575 false); // No number check. | |
| 1576 ReplaceCall(call, check_cid); | 1452 ReplaceCall(call, check_cid); |
| 1577 return; | 1453 return; |
| 1578 } | 1454 } |
| 1579 | 1455 |
| 1580 InstanceOfInstr* instance_of = | 1456 InstanceOfInstr* instance_of = new (Z) |
| 1581 new(Z) InstanceOfInstr(call->token_pos(), | 1457 InstanceOfInstr(call->token_pos(), new (Z) Value(left), |
| 1582 new(Z) Value(left), | 1458 new (Z) Value(type_args), type, negate, call->deopt_id()); |
| 1583 new(Z) Value(type_args), | |
| 1584 type, | |
| 1585 negate, | |
| 1586 call->deopt_id()); | |
| 1587 ReplaceCall(call, instance_of); | 1459 ReplaceCall(call, instance_of); |
| 1588 } | 1460 } |
| 1589 | 1461 |
| 1590 | 1462 |
| 1591 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). | 1463 // TODO(srdjan): Apply optimizations as in ReplaceWithInstanceOf (TestCids). |
| 1592 void JitOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { | 1464 void JitOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { |
| 1593 ASSERT(Token::IsTypeCastOperator(call->token_kind())); | 1465 ASSERT(Token::IsTypeCastOperator(call->token_kind())); |
| 1594 Definition* left = call->ArgumentAt(0); | 1466 Definition* left = call->ArgumentAt(0); |
| 1595 Definition* type_args = call->ArgumentAt(1); | 1467 Definition* type_args = call->ArgumentAt(1); |
| 1596 const AbstractType& type = | 1468 const AbstractType& type = |
| 1597 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()); | 1469 AbstractType::Cast(call->ArgumentAt(2)->AsConstant()->value()); |
| 1598 ASSERT(!type.IsMalformedOrMalbounded()); | 1470 ASSERT(!type.IsMalformedOrMalbounded()); |
| 1599 const ICData& unary_checks = | 1471 const ICData& unary_checks = |
| 1600 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); | 1472 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()); |
| 1601 if ((unary_checks.NumberOfChecks() > 0) && | 1473 if ((unary_checks.NumberOfChecks() > 0) && |
| 1602 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { | 1474 (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { |
| 1603 ZoneGrowableArray<intptr_t>* results = | 1475 ZoneGrowableArray<intptr_t>* results = |
| 1604 new(Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2); | 1476 new (Z) ZoneGrowableArray<intptr_t>(unary_checks.NumberOfChecks() * 2); |
| 1605 const Bool& as_bool = Bool::ZoneHandle(Z, | 1477 const Bool& as_bool = |
| 1606 InstanceOfAsBool(unary_checks, type, results)); | 1478 Bool::ZoneHandle(Z, InstanceOfAsBool(unary_checks, type, results)); |
| 1607 if (as_bool.raw() == Bool::True().raw()) { | 1479 if (as_bool.raw() == Bool::True().raw()) { |
| 1608 AddReceiverCheck(call); | 1480 AddReceiverCheck(call); |
| 1609 // Remove the original push arguments. | 1481 // Remove the original push arguments. |
| 1610 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 1482 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 1611 PushArgumentInstr* push = call->PushArgumentAt(i); | 1483 PushArgumentInstr* push = call->PushArgumentAt(i); |
| 1612 push->ReplaceUsesWith(push->value()->definition()); | 1484 push->ReplaceUsesWith(push->value()->definition()); |
| 1613 push->RemoveFromGraph(); | 1485 push->RemoveFromGraph(); |
| 1614 } | 1486 } |
| 1615 // Remove call, replace it with 'left'. | 1487 // Remove call, replace it with 'left'. |
| 1616 call->ReplaceUsesWith(left); | 1488 call->ReplaceUsesWith(left); |
| 1617 ASSERT(current_iterator()->Current() == call); | 1489 ASSERT(current_iterator()->Current() == call); |
| 1618 current_iterator()->RemoveCurrentFromGraph(); | 1490 current_iterator()->RemoveCurrentFromGraph(); |
| 1619 return; | 1491 return; |
| 1620 } | 1492 } |
| 1621 } | 1493 } |
| 1622 AssertAssignableInstr* assert_as = | 1494 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( |
| 1623 new(Z) AssertAssignableInstr(call->token_pos(), | 1495 call->token_pos(), new (Z) Value(left), new (Z) Value(type_args), type, |
| 1624 new(Z) Value(left), | 1496 Symbols::InTypeCast(), call->deopt_id()); |
| 1625 new(Z) Value(type_args), | |
| 1626 type, | |
| 1627 Symbols::InTypeCast(), | |
| 1628 call->deopt_id()); | |
| 1629 ReplaceCall(call, assert_as); | 1497 ReplaceCall(call, assert_as); |
| 1630 } | 1498 } |
| 1631 | 1499 |
| 1632 | 1500 |
| 1633 // Tries to optimize instance call by replacing it with a faster instruction | 1501 // Tries to optimize instance call by replacing it with a faster instruction |
| 1634 // (e.g, binary op, field load, ..). | 1502 // (e.g, binary op, field load, ..). |
| 1635 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1503 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 1636 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | 1504 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { |
| 1637 return; | 1505 return; |
| 1638 } | 1506 } |
| 1639 const Token::Kind op_kind = instr->token_kind(); | 1507 const Token::Kind op_kind = instr->token_kind(); |
| 1640 | 1508 |
| 1641 // Type test is special as it always gets converted into inlined code. | 1509 // Type test is special as it always gets converted into inlined code. |
| 1642 if (Token::IsTypeTestOperator(op_kind)) { | 1510 if (Token::IsTypeTestOperator(op_kind)) { |
| 1643 ReplaceWithInstanceOf(instr); | 1511 ReplaceWithInstanceOf(instr); |
| 1644 return; | 1512 return; |
| 1645 } | 1513 } |
| 1646 | 1514 |
| 1647 if (Token::IsTypeCastOperator(op_kind)) { | 1515 if (Token::IsTypeCastOperator(op_kind)) { |
| 1648 ReplaceWithTypeCast(instr); | 1516 ReplaceWithTypeCast(instr); |
| 1649 return; | 1517 return; |
| 1650 } | 1518 } |
| 1651 | 1519 |
| 1652 const ICData& unary_checks = | 1520 const ICData& unary_checks = |
| 1653 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | 1521 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
| 1654 | 1522 |
| 1655 const bool is_dense = CheckClassInstr::IsDenseCidRange(unary_checks); | 1523 const bool is_dense = CheckClassInstr::IsDenseCidRange(unary_checks); |
| 1656 const intptr_t max_checks = (op_kind == Token::kEQ) | 1524 const intptr_t max_checks = (op_kind == Token::kEQ) |
| 1657 ? FLAG_max_equality_polymorphic_checks | 1525 ? FLAG_max_equality_polymorphic_checks |
| 1658 : FLAG_max_polymorphic_checks; | 1526 : FLAG_max_polymorphic_checks; |
| 1659 if ((unary_checks.NumberOfChecks() > max_checks) && | 1527 if ((unary_checks.NumberOfChecks() > max_checks) && !is_dense && |
| 1660 !is_dense && | |
| 1661 flow_graph()->InstanceCallNeedsClassCheck( | 1528 flow_graph()->InstanceCallNeedsClassCheck( |
| 1662 instr, RawFunction::kRegularFunction)) { | 1529 instr, RawFunction::kRegularFunction)) { |
| 1663 // Too many checks, it will be megamorphic which needs unary checks. | 1530 // Too many checks, it will be megamorphic which needs unary checks. |
| 1664 instr->set_ic_data(&unary_checks); | 1531 instr->set_ic_data(&unary_checks); |
| 1665 return; | 1532 return; |
| 1666 } | 1533 } |
| 1667 | 1534 |
| 1668 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { | 1535 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { |
| 1669 return; | 1536 return; |
| 1670 } | 1537 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1698 } | 1565 } |
| 1699 if (TryInlineInstanceMethod(instr)) { | 1566 if (TryInlineInstanceMethod(instr)) { |
| 1700 return; | 1567 return; |
| 1701 } | 1568 } |
| 1702 | 1569 |
| 1703 bool has_one_target = unary_checks.HasOneTarget(); | 1570 bool has_one_target = unary_checks.HasOneTarget(); |
| 1704 | 1571 |
| 1705 if (has_one_target) { | 1572 if (has_one_target) { |
| 1706 // Check if the single target is a polymorphic target, if it is, | 1573 // Check if the single target is a polymorphic target, if it is, |
| 1707 // we don't have one target. | 1574 // we don't have one target. |
| 1708 const Function& target = | 1575 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1709 Function::Handle(Z, unary_checks.GetTargetAt(0)); | |
| 1710 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { | 1576 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
| 1711 has_one_target = | 1577 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
| 1712 PolymorphicInstanceCallInstr::ComputeRuntimeType(unary_checks) != | 1578 unary_checks) != Type::null(); |
| 1713 Type::null(); | |
| 1714 } else { | 1579 } else { |
| 1715 const bool polymorphic_target = | 1580 const bool polymorphic_target = |
| 1716 MethodRecognizer::PolymorphicTarget(target); | 1581 MethodRecognizer::PolymorphicTarget(target); |
| 1717 has_one_target = !polymorphic_target; | 1582 has_one_target = !polymorphic_target; |
| 1718 } | 1583 } |
| 1719 } | 1584 } |
| 1720 | 1585 |
| 1721 if (has_one_target) { | 1586 if (has_one_target) { |
| 1722 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1587 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1723 const RawFunction::Kind function_kind = target.kind(); | 1588 const RawFunction::Kind function_kind = target.kind(); |
| 1724 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1589 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 1725 PolymorphicInstanceCallInstr* call = | 1590 PolymorphicInstanceCallInstr* call = |
| 1726 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 1591 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 1727 /* call_with_checks = */ false, | 1592 /* call_with_checks = */ false, |
| 1728 /* complete = */ false); | 1593 /* complete = */ false); |
| 1729 instr->ReplaceWith(call, current_iterator()); | 1594 instr->ReplaceWith(call, current_iterator()); |
| 1730 return; | 1595 return; |
| 1731 } | 1596 } |
| 1732 } | 1597 } |
| 1733 | 1598 |
| 1734 if ((unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) || | 1599 if ((unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) || |
| 1735 (has_one_target && is_dense)) { | 1600 (has_one_target && is_dense)) { |
| 1736 bool call_with_checks; | 1601 bool call_with_checks; |
| 1737 if (has_one_target && FLAG_polymorphic_with_deopt) { | 1602 if (has_one_target && FLAG_polymorphic_with_deopt) { |
| 1738 // Type propagation has not run yet, we cannot eliminate the check. | 1603 // Type propagation has not run yet, we cannot eliminate the check. |
| 1739 AddReceiverCheck(instr); | 1604 AddReceiverCheck(instr); |
| 1740 // Call can still deoptimize, do not detach environment from instr. | 1605 // Call can still deoptimize, do not detach environment from instr. |
| 1741 call_with_checks = false; | 1606 call_with_checks = false; |
| 1742 } else { | 1607 } else { |
| 1743 call_with_checks = true; | 1608 call_with_checks = true; |
| 1744 } | 1609 } |
| 1745 PolymorphicInstanceCallInstr* call = | 1610 PolymorphicInstanceCallInstr* call = new (Z) |
| 1746 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 1611 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, |
| 1747 call_with_checks, | 1612 /* complete = */ false); |
| 1748 /* complete = */ false); | |
| 1749 instr->ReplaceWith(call, current_iterator()); | 1613 instr->ReplaceWith(call, current_iterator()); |
| 1750 } | 1614 } |
| 1751 } | 1615 } |
| 1752 | 1616 |
| 1753 | 1617 |
| 1754 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { | 1618 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { |
| 1755 MethodRecognizer::Kind recognized_kind = | 1619 MethodRecognizer::Kind recognized_kind = |
| 1756 MethodRecognizer::RecognizeKind(call->function()); | 1620 MethodRecognizer::RecognizeKind(call->function()); |
| 1757 switch (recognized_kind) { | 1621 switch (recognized_kind) { |
| 1758 case MethodRecognizer::kObjectConstructor: | 1622 case MethodRecognizer::kObjectConstructor: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1776 case MethodRecognizer::kMathAcos: | 1640 case MethodRecognizer::kMathAcos: |
| 1777 case MethodRecognizer::kMathAtan: | 1641 case MethodRecognizer::kMathAtan: |
| 1778 case MethodRecognizer::kMathAtan2: | 1642 case MethodRecognizer::kMathAtan2: |
| 1779 FlowGraphInliner::TryReplaceStaticCallWithInline( | 1643 FlowGraphInliner::TryReplaceStaticCallWithInline( |
| 1780 flow_graph_, current_iterator(), call); | 1644 flow_graph_, current_iterator(), call); |
| 1781 break; | 1645 break; |
| 1782 case MethodRecognizer::kMathMin: | 1646 case MethodRecognizer::kMathMin: |
| 1783 case MethodRecognizer::kMathMax: { | 1647 case MethodRecognizer::kMathMax: { |
| 1784 // We can handle only monomorphic min/max call sites with both arguments | 1648 // We can handle only monomorphic min/max call sites with both arguments |
| 1785 // being either doubles or smis. | 1649 // being either doubles or smis. |
| 1786 if (CanUnboxDouble() && | 1650 if (CanUnboxDouble() && call->HasICData() && |
| 1787 call->HasICData() && | |
| 1788 (call->ic_data()->NumberOfChecks() == 1)) { | 1651 (call->ic_data()->NumberOfChecks() == 1)) { |
| 1789 const ICData& ic_data = *call->ic_data(); | 1652 const ICData& ic_data = *call->ic_data(); |
| 1790 intptr_t result_cid = kIllegalCid; | 1653 intptr_t result_cid = kIllegalCid; |
| 1791 if (ICDataHasReceiverArgumentClassIds(ic_data, | 1654 if (ICDataHasReceiverArgumentClassIds(ic_data, kDoubleCid, |
| 1792 kDoubleCid, kDoubleCid)) { | 1655 kDoubleCid)) { |
| 1793 result_cid = kDoubleCid; | 1656 result_cid = kDoubleCid; |
| 1794 } else if (ICDataHasReceiverArgumentClassIds(ic_data, | 1657 } else if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, |
| 1795 kSmiCid, kSmiCid)) { | 1658 kSmiCid)) { |
| 1796 result_cid = kSmiCid; | 1659 result_cid = kSmiCid; |
| 1797 } | 1660 } |
| 1798 if (result_cid != kIllegalCid) { | 1661 if (result_cid != kIllegalCid) { |
| 1799 MathMinMaxInstr* min_max = new(Z) MathMinMaxInstr( | 1662 MathMinMaxInstr* min_max = new (Z) MathMinMaxInstr( |
| 1800 recognized_kind, | 1663 recognized_kind, new (Z) Value(call->ArgumentAt(0)), |
| 1801 new(Z) Value(call->ArgumentAt(0)), | 1664 new (Z) Value(call->ArgumentAt(1)), call->deopt_id(), result_cid); |
| 1802 new(Z) Value(call->ArgumentAt(1)), | |
| 1803 call->deopt_id(), | |
| 1804 result_cid); | |
| 1805 const ICData& unary_checks = | 1665 const ICData& unary_checks = |
| 1806 ICData::ZoneHandle(Z, ic_data.AsUnaryClassChecks()); | 1666 ICData::ZoneHandle(Z, ic_data.AsUnaryClassChecks()); |
| 1807 AddCheckClass(min_max->left()->definition(), | 1667 AddCheckClass(min_max->left()->definition(), unary_checks, |
| 1808 unary_checks, | 1668 call->deopt_id(), call->env(), call); |
| 1809 call->deopt_id(), | 1669 AddCheckClass(min_max->right()->definition(), unary_checks, |
| 1810 call->env(), | 1670 call->deopt_id(), call->env(), call); |
| 1811 call); | |
| 1812 AddCheckClass(min_max->right()->definition(), | |
| 1813 unary_checks, | |
| 1814 call->deopt_id(), | |
| 1815 call->env(), | |
| 1816 call); | |
| 1817 ReplaceCall(call, min_max); | 1671 ReplaceCall(call, min_max); |
| 1818 } | 1672 } |
| 1819 } | 1673 } |
| 1820 break; | 1674 break; |
| 1821 } | 1675 } |
| 1822 | 1676 |
| 1823 case MethodRecognizer::kDoubleFromInteger: { | 1677 case MethodRecognizer::kDoubleFromInteger: { |
| 1824 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) { | 1678 if (call->HasICData() && (call->ic_data()->NumberOfChecks() == 1)) { |
| 1825 const ICData& ic_data = *call->ic_data(); | 1679 const ICData& ic_data = *call->ic_data(); |
| 1826 if (CanUnboxDouble()) { | 1680 if (CanUnboxDouble()) { |
| 1827 if (ArgIsAlways(kSmiCid, ic_data, 1)) { | 1681 if (ArgIsAlways(kSmiCid, ic_data, 1)) { |
| 1828 Definition* arg = call->ArgumentAt(1); | 1682 Definition* arg = call->ArgumentAt(1); |
| 1829 AddCheckSmi(arg, call->deopt_id(), call->env(), call); | 1683 AddCheckSmi(arg, call->deopt_id(), call->env(), call); |
| 1830 ReplaceCall(call, | 1684 ReplaceCall(call, new (Z) SmiToDoubleInstr(new (Z) Value(arg), |
| 1831 new(Z) SmiToDoubleInstr(new(Z) Value(arg), | 1685 call->token_pos())); |
| 1832 call->token_pos())); | |
| 1833 } else if (ArgIsAlways(kMintCid, ic_data, 1) && | 1686 } else if (ArgIsAlways(kMintCid, ic_data, 1) && |
| 1834 CanConvertUnboxedMintToDouble()) { | 1687 CanConvertUnboxedMintToDouble()) { |
| 1835 Definition* arg = call->ArgumentAt(1); | 1688 Definition* arg = call->ArgumentAt(1); |
| 1836 ReplaceCall(call, | 1689 ReplaceCall(call, new (Z) MintToDoubleInstr(new (Z) Value(arg), |
| 1837 new(Z) MintToDoubleInstr(new(Z) Value(arg), | 1690 call->deopt_id())); |
| 1838 call->deopt_id())); | |
| 1839 } | 1691 } |
| 1840 } | 1692 } |
| 1841 } | 1693 } |
| 1842 break; | 1694 break; |
| 1843 } | 1695 } |
| 1844 default: | 1696 default: |
| 1845 break; | 1697 break; |
| 1846 } | 1698 } |
| 1847 } | 1699 } |
| 1848 | 1700 |
| 1849 | 1701 |
| 1850 void JitOptimizer::VisitStoreInstanceField( | 1702 void JitOptimizer::VisitStoreInstanceField(StoreInstanceFieldInstr* instr) { |
| 1851 StoreInstanceFieldInstr* instr) { | |
| 1852 if (instr->IsUnboxedStore()) { | 1703 if (instr->IsUnboxedStore()) { |
| 1853 // Determine if this field should be unboxed based on the usage of getter | 1704 // Determine if this field should be unboxed based on the usage of getter |
| 1854 // and setter functions: The heuristic requires that the setter has a | 1705 // and setter functions: The heuristic requires that the setter has a |
| 1855 // usage count of at least 1/kGetterSetterRatio of the getter usage count. | 1706 // usage count of at least 1/kGetterSetterRatio of the getter usage count. |
| 1856 // This is to avoid unboxing fields where the setter is never or rarely | 1707 // This is to avoid unboxing fields where the setter is never or rarely |
| 1857 // executed. | 1708 // executed. |
| 1858 const Field& field = instr->field(); | 1709 const Field& field = instr->field(); |
| 1859 const String& field_name = String::Handle(Z, field.name()); | 1710 const String& field_name = String::Handle(Z, field.name()); |
| 1860 const Class& owner = Class::Handle(Z, field.Owner()); | 1711 const Class& owner = Class::Handle(Z, field.Owner()); |
| 1861 const Function& getter = | 1712 const Function& getter = |
| 1862 Function::Handle(Z, owner.LookupGetterFunction(field_name)); | 1713 Function::Handle(Z, owner.LookupGetterFunction(field_name)); |
| 1863 const Function& setter = | 1714 const Function& setter = |
| 1864 Function::Handle(Z, owner.LookupSetterFunction(field_name)); | 1715 Function::Handle(Z, owner.LookupSetterFunction(field_name)); |
| 1865 bool unboxed_field = false; | 1716 bool unboxed_field = false; |
| 1866 if (!getter.IsNull() && !setter.IsNull()) { | 1717 if (!getter.IsNull() && !setter.IsNull()) { |
| 1867 if (field.is_double_initialized()) { | 1718 if (field.is_double_initialized()) { |
| 1868 unboxed_field = true; | 1719 unboxed_field = true; |
| 1869 } else if ((setter.usage_counter() > 0) && | 1720 } else if ((setter.usage_counter() > 0) && |
| 1870 ((FLAG_getter_setter_ratio * setter.usage_counter()) >= | 1721 ((FLAG_getter_setter_ratio * setter.usage_counter()) >= |
| 1871 getter.usage_counter())) { | 1722 getter.usage_counter())) { |
| 1872 unboxed_field = true; | 1723 unboxed_field = true; |
| 1873 } | 1724 } |
| 1874 } | 1725 } |
| 1875 if (!unboxed_field) { | 1726 if (!unboxed_field) { |
| 1876 // TODO(srdjan): Instead of aborting pass this field to the mutator thread | 1727 // TODO(srdjan): Instead of aborting pass this field to the mutator thread |
| 1877 // so that it can: | 1728 // so that it can: |
| 1878 // - set it to unboxed | 1729 // - set it to unboxed |
| 1879 // - deoptimize dependent code. | 1730 // - deoptimize dependent code. |
| 1880 if (Compiler::IsBackgroundCompilation()) { | 1731 if (Compiler::IsBackgroundCompilation()) { |
| 1881 isolate()->AddDeoptimizingBoxedField(field); | 1732 isolate()->AddDeoptimizingBoxedField(field); |
| 1882 Compiler::AbortBackgroundCompilation(Thread::kNoDeoptId, | 1733 Compiler::AbortBackgroundCompilation( |
| 1883 "Unboxing instance field while compiling"); | 1734 Thread::kNoDeoptId, "Unboxing instance field while compiling"); |
| 1884 UNREACHABLE(); | 1735 UNREACHABLE(); |
| 1885 } | 1736 } |
| 1886 if (FLAG_trace_optimization || FLAG_trace_field_guards) { | 1737 if (FLAG_trace_optimization || FLAG_trace_field_guards) { |
| 1887 THR_Print("Disabling unboxing of %s\n", field.ToCString()); | 1738 THR_Print("Disabling unboxing of %s\n", field.ToCString()); |
| 1888 if (!setter.IsNull()) { | 1739 if (!setter.IsNull()) { |
| 1889 OS::Print(" setter usage count: %" Pd "\n", setter.usage_counter()); | 1740 OS::Print(" setter usage count: %" Pd "\n", setter.usage_counter()); |
| 1890 } | 1741 } |
| 1891 if (!getter.IsNull()) { | 1742 if (!getter.IsNull()) { |
| 1892 OS::Print(" getter usage count: %" Pd "\n", getter.usage_counter()); | 1743 OS::Print(" getter usage count: %" Pd "\n", getter.usage_counter()); |
| 1893 } | 1744 } |
| 1894 } | 1745 } |
| 1895 ASSERT(field.IsOriginal()); | 1746 ASSERT(field.IsOriginal()); |
| 1896 field.set_is_unboxing_candidate(false); | 1747 field.set_is_unboxing_candidate(false); |
| 1897 field.DeoptimizeDependentCode(); | 1748 field.DeoptimizeDependentCode(); |
| 1898 } else { | 1749 } else { |
| 1899 flow_graph()->parsed_function().AddToGuardedFields(&field); | 1750 flow_graph()->parsed_function().AddToGuardedFields(&field); |
| 1900 } | 1751 } |
| 1901 } | 1752 } |
| 1902 } | 1753 } |
| 1903 | 1754 |
| 1904 | 1755 |
| 1905 void JitOptimizer::VisitAllocateContext(AllocateContextInstr* instr) { | 1756 void JitOptimizer::VisitAllocateContext(AllocateContextInstr* instr) { |
| 1906 // Replace generic allocation with a sequence of inlined allocation and | 1757 // Replace generic allocation with a sequence of inlined allocation and |
| 1907 // explicit initalizing stores. | 1758 // explicit initalizing stores. |
| 1908 AllocateUninitializedContextInstr* replacement = | 1759 AllocateUninitializedContextInstr* replacement = |
| 1909 new AllocateUninitializedContextInstr(instr->token_pos(), | 1760 new AllocateUninitializedContextInstr(instr->token_pos(), |
| 1910 instr->num_context_variables()); | 1761 instr->num_context_variables()); |
| 1911 instr->ReplaceWith(replacement, current_iterator()); | 1762 instr->ReplaceWith(replacement, current_iterator()); |
| 1912 | 1763 |
| 1913 StoreInstanceFieldInstr* store = | 1764 StoreInstanceFieldInstr* store = new (Z) |
| 1914 new(Z) StoreInstanceFieldInstr(Context::parent_offset(), | 1765 StoreInstanceFieldInstr(Context::parent_offset(), new Value(replacement), |
| 1915 new Value(replacement), | 1766 new Value(flow_graph_->constant_null()), |
| 1916 new Value(flow_graph_->constant_null()), | 1767 kNoStoreBarrier, instr->token_pos()); |
| 1917 kNoStoreBarrier, | |
| 1918 instr->token_pos()); | |
| 1919 // Storing into uninitialized memory; remember to prevent dead store | 1768 // Storing into uninitialized memory; remember to prevent dead store |
| 1920 // elimination and ensure proper GC barrier. | 1769 // elimination and ensure proper GC barrier. |
| 1921 store->set_is_initialization(true); | 1770 store->set_is_initialization(true); |
| 1922 flow_graph_->InsertAfter(replacement, store, NULL, FlowGraph::kEffect); | 1771 flow_graph_->InsertAfter(replacement, store, NULL, FlowGraph::kEffect); |
| 1923 Definition* cursor = store; | 1772 Definition* cursor = store; |
| 1924 for (intptr_t i = 0; i < instr->num_context_variables(); ++i) { | 1773 for (intptr_t i = 0; i < instr->num_context_variables(); ++i) { |
| 1925 store = | 1774 store = new (Z) StoreInstanceFieldInstr( |
| 1926 new(Z) StoreInstanceFieldInstr(Context::variable_offset(i), | 1775 Context::variable_offset(i), new Value(replacement), |
| 1927 new Value(replacement), | 1776 new Value(flow_graph_->constant_null()), kNoStoreBarrier, |
| 1928 new Value(flow_graph_->constant_null()), | 1777 instr->token_pos()); |
| 1929 kNoStoreBarrier, | |
| 1930 instr->token_pos()); | |
| 1931 // Storing into uninitialized memory; remember to prevent dead store | 1778 // Storing into uninitialized memory; remember to prevent dead store |
| 1932 // elimination and ensure proper GC barrier. | 1779 // elimination and ensure proper GC barrier. |
| 1933 store->set_is_initialization(true); | 1780 store->set_is_initialization(true); |
| 1934 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect); | 1781 flow_graph_->InsertAfter(cursor, store, NULL, FlowGraph::kEffect); |
| 1935 cursor = store; | 1782 cursor = store; |
| 1936 } | 1783 } |
| 1937 } | 1784 } |
| 1938 | 1785 |
| 1939 | 1786 |
| 1940 void JitOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { | 1787 void JitOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { |
| 1941 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. | 1788 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. |
| 1942 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) | 1789 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) |
| 1943 if (!instr->can_pack_into_smi()) | 1790 if (!instr->can_pack_into_smi()) instr->set_representation(kUnboxedMint); |
| 1944 instr->set_representation(kUnboxedMint); | |
| 1945 #endif | 1791 #endif |
| 1946 } | 1792 } |
| 1947 | 1793 |
| 1948 | 1794 |
| 1949 bool JitOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, | 1795 bool JitOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, |
| 1950 const ICData& unary_ic_data) { | 1796 const ICData& unary_ic_data) { |
| 1951 ASSERT((unary_ic_data.NumberOfChecks() > 0) && | 1797 ASSERT((unary_ic_data.NumberOfChecks() > 0) && |
| 1952 (unary_ic_data.NumArgsTested() == 1)); | 1798 (unary_ic_data.NumArgsTested() == 1)); |
| 1953 if (I->type_checks()) { | 1799 if (I->type_checks()) { |
| 1954 // Checked mode setters are inlined like normal methods by conventional | 1800 // Checked mode setters are inlined like normal methods by conventional |
| 1955 // inlining. | 1801 // inlining. |
| 1956 return false; | 1802 return false; |
| 1957 } | 1803 } |
| 1958 | 1804 |
| 1959 ASSERT(instr->HasICData()); | 1805 ASSERT(instr->HasICData()); |
| 1960 if (unary_ic_data.NumberOfChecks() == 0) { | 1806 if (unary_ic_data.NumberOfChecks() == 0) { |
| 1961 // No type feedback collected. | 1807 // No type feedback collected. |
| 1962 return false; | 1808 return false; |
| 1963 } | 1809 } |
| 1964 if (!unary_ic_data.HasOneTarget()) { | 1810 if (!unary_ic_data.HasOneTarget()) { |
| 1965 // Polymorphic sites are inlined like normal method calls by conventional | 1811 // Polymorphic sites are inlined like normal method calls by conventional |
| 1966 // inlining. | 1812 // inlining. |
| 1967 return false; | 1813 return false; |
| 1968 } | 1814 } |
| 1969 Function& target = Function::Handle(Z); | 1815 Function& target = Function::Handle(Z); |
| 1970 intptr_t class_id; | 1816 intptr_t class_id; |
| 1971 unary_ic_data.GetOneClassCheckAt(0, &class_id, &target); | 1817 unary_ic_data.GetOneClassCheckAt(0, &class_id, &target); |
| 1972 if (target.kind() != RawFunction::kImplicitSetter) { | 1818 if (target.kind() != RawFunction::kImplicitSetter) { |
| 1973 // Non-implicit setter are inlined like normal method calls. | 1819 // Non-implicit setter are inlined like normal method calls. |
| 1974 return false; | 1820 return false; |
| 1975 } | 1821 } |
| 1976 // Inline implicit instance setter. | 1822 // Inline implicit instance setter. |
| 1977 const String& field_name = | 1823 const String& field_name = |
| 1978 String::Handle(Z, Field::NameFromSetter(instr->function_name())); | 1824 String::Handle(Z, Field::NameFromSetter(instr->function_name())); |
| 1979 const Field& field = | 1825 const Field& field = Field::ZoneHandle(Z, GetField(class_id, field_name)); |
| 1980 Field::ZoneHandle(Z, GetField(class_id, field_name)); | |
| 1981 ASSERT(!field.IsNull()); | 1826 ASSERT(!field.IsNull()); |
| 1982 | 1827 |
| 1983 if (flow_graph()->InstanceCallNeedsClassCheck( | 1828 if (flow_graph()->InstanceCallNeedsClassCheck(instr, |
| 1984 instr, RawFunction::kImplicitSetter)) { | 1829 RawFunction::kImplicitSetter)) { |
| 1985 AddReceiverCheck(instr); | 1830 AddReceiverCheck(instr); |
| 1986 } | 1831 } |
| 1987 if (field.guarded_cid() != kDynamicCid) { | 1832 if (field.guarded_cid() != kDynamicCid) { |
| 1988 ASSERT(FLAG_use_field_guards); | 1833 ASSERT(FLAG_use_field_guards); |
| 1989 InsertBefore(instr, | 1834 InsertBefore( |
| 1990 new(Z) GuardFieldClassInstr( | 1835 instr, new (Z) GuardFieldClassInstr(new (Z) Value(instr->ArgumentAt(1)), |
| 1991 new(Z) Value(instr->ArgumentAt(1)), | 1836 field, instr->deopt_id()), |
| 1992 field, | 1837 instr->env(), FlowGraph::kEffect); |
| 1993 instr->deopt_id()), | |
| 1994 instr->env(), | |
| 1995 FlowGraph::kEffect); | |
| 1996 } | 1838 } |
| 1997 | 1839 |
| 1998 if (field.needs_length_check()) { | 1840 if (field.needs_length_check()) { |
| 1999 ASSERT(FLAG_use_field_guards); | 1841 ASSERT(FLAG_use_field_guards); |
| 2000 InsertBefore(instr, | 1842 InsertBefore(instr, new (Z) GuardFieldLengthInstr( |
| 2001 new(Z) GuardFieldLengthInstr( | 1843 new (Z) Value(instr->ArgumentAt(1)), field, |
| 2002 new(Z) Value(instr->ArgumentAt(1)), | 1844 instr->deopt_id()), |
| 2003 field, | 1845 instr->env(), FlowGraph::kEffect); |
| 2004 instr->deopt_id()), | |
| 2005 instr->env(), | |
| 2006 FlowGraph::kEffect); | |
| 2007 } | 1846 } |
| 2008 | 1847 |
| 2009 // Field guard was detached. | 1848 // Field guard was detached. |
| 2010 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr( | 1849 StoreInstanceFieldInstr* store = new (Z) |
| 2011 field, | 1850 StoreInstanceFieldInstr(field, new (Z) Value(instr->ArgumentAt(0)), |
| 2012 new(Z) Value(instr->ArgumentAt(0)), | 1851 new (Z) Value(instr->ArgumentAt(1)), |
| 2013 new(Z) Value(instr->ArgumentAt(1)), | 1852 kEmitStoreBarrier, instr->token_pos()); |
| 2014 kEmitStoreBarrier, | |
| 2015 instr->token_pos()); | |
| 2016 | 1853 |
| 2017 if (store->IsUnboxedStore()) { | 1854 if (store->IsUnboxedStore()) { |
| 2018 flow_graph()->parsed_function().AddToGuardedFields(&field); | 1855 flow_graph()->parsed_function().AddToGuardedFields(&field); |
| 2019 } | 1856 } |
| 2020 | 1857 |
| 2021 // Discard the environment from the original instruction because the store | 1858 // Discard the environment from the original instruction because the store |
| 2022 // can't deoptimize. | 1859 // can't deoptimize. |
| 2023 instr->RemoveEnvironment(); | 1860 instr->RemoveEnvironment(); |
| 2024 ReplaceCall(instr, store); | 1861 ReplaceCall(instr, store); |
| 2025 return true; | 1862 return true; |
| 2026 } | 1863 } |
| 2027 | 1864 |
| 2028 | 1865 |
| 2029 } // namespace dart | 1866 } // namespace dart |
| OLD | NEW |