Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 10 #include "vm/cpu.h" | 10 #include "vm/cpu.h" |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 180 // TryCreateICData is only invoked if the ic_data target has not been called | 180 // TryCreateICData is only invoked if the ic_data target has not been called |
| 181 // yet, so no warning can possibly have been issued. | 181 // yet, so no warning can possibly have been issued. |
| 182 ASSERT(!call->ic_data()->IssuedJSWarning()); | 182 ASSERT(!call->ic_data()->IssuedJSWarning()); |
| 183 if (call->ic_data()->MayCheckForJSWarning()) { | 183 if (call->ic_data()->MayCheckForJSWarning()) { |
| 184 return false; | 184 return false; |
| 185 } | 185 } |
| 186 } | 186 } |
| 187 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); | 187 GrowableArray<intptr_t> class_ids(call->ic_data()->NumArgsTested()); |
| 188 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); | 188 ASSERT(call->ic_data()->NumArgsTested() <= call->ArgumentCount()); |
| 189 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { | 189 for (intptr_t i = 0; i < call->ic_data()->NumArgsTested(); i++) { |
| 190 const intptr_t cid = call->PushArgumentAt(i)->value()->Type()->ToCid(); | 190 class_ids.Add(call->PushArgumentAt(i)->value()->Type()->ToCid()); |
| 191 class_ids.Add(cid); | |
| 192 } | 191 } |
| 193 | 192 |
| 194 const Token::Kind op_kind = call->token_kind(); | 193 const Token::Kind op_kind = call->token_kind(); |
| 195 if (Token::IsRelationalOperator(op_kind) || | 194 if (Token::IsRelationalOperator(op_kind) || |
| 196 Token::IsEqualityOperator(op_kind) || | 195 Token::IsEqualityOperator(op_kind) || |
| 197 Token::IsBinaryOperator(op_kind)) { | 196 Token::IsBinaryOperator(op_kind)) { |
| 198 // Guess cid: if one of the inputs is a number assume that the other | 197 // Guess cid: if one of the inputs is a number assume that the other |
| 199 // is a number of same type. | 198 // is a number of same type. |
| 200 if (FLAG_guess_other_cid) { | 199 if (FLAG_guess_other_cid) { |
| 201 const intptr_t cid_0 = class_ids[0]; | 200 const intptr_t cid_0 = class_ids[0]; |
| 202 const intptr_t cid_1 = class_ids[1]; | 201 const intptr_t cid_1 = class_ids[1]; |
| 203 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) { | 202 if ((cid_0 == kDynamicCid) && (IsNumberCid(cid_1))) { |
| 204 class_ids[0] = cid_1; | 203 class_ids[0] = cid_1; |
| 205 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) { | 204 } else if (IsNumberCid(cid_0) && (cid_1 == kDynamicCid)) { |
| 206 class_ids[1] = cid_0; | 205 class_ids[1] = cid_0; |
| 207 } | 206 } |
| 208 } | 207 } |
| 209 } | 208 } |
| 210 | 209 |
| 211 bool all_cids_known = true; | 210 bool all_cids_known = true; |
| 212 for (intptr_t i = 0; i < class_ids.length(); i++) { | 211 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 213 if (class_ids[i] == kDynamicCid) { | 212 if (class_ids[i] == kDynamicCid) { |
| 214 // Not all cid-s known. | 213 // Not all cid-s known. |
| 215 all_cids_known = false; | 214 all_cids_known = false; |
| 216 break; | 215 break; |
| 217 } | 216 } |
| 218 } | 217 } |
| 219 | 218 |
| 220 if (all_cids_known) { | 219 if (all_cids_known) { |
| 221 const Array& args_desc_array = Array::Handle(Z, | |
| 222 ArgumentsDescriptor::New(call->ArgumentCount(), | |
| 223 call->argument_names())); | |
| 224 ArgumentsDescriptor args_desc(args_desc_array); | |
| 225 const Class& receiver_class = Class::Handle(Z, | 220 const Class& receiver_class = Class::Handle(Z, |
| 226 isolate()->class_table()->At(class_ids[0])); | 221 isolate()->class_table()->At(class_ids[0])); |
| 227 if (!receiver_class.is_finalized()) { | 222 if (!receiver_class.is_finalized()) { |
| 228 // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can | 223 // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can |
| 229 // cause class finalization, since callee's receiver class may not be | 224 // cause class finalization, since callee's receiver class may not be |
| 230 // finalized yet. | 225 // finalized yet. |
| 231 return false; | 226 return false; |
| 232 } | 227 } |
| 228 const Array& args_desc_array = Array::Handle(Z, | |
| 229 ArgumentsDescriptor::New(call->ArgumentCount(), | |
| 230 call->argument_names())); | |
| 231 ArgumentsDescriptor args_desc(args_desc_array); | |
| 233 const Function& function = Function::Handle(Z, | 232 const Function& function = Function::Handle(Z, |
| 234 Resolver::ResolveDynamicForReceiverClass( | 233 Resolver::ResolveDynamicForReceiverClass( |
| 235 receiver_class, | 234 receiver_class, |
| 236 call->function_name(), | 235 call->function_name(), |
| 237 args_desc)); | 236 args_desc)); |
| 238 if (function.IsNull()) { | 237 if (function.IsNull()) { |
| 239 return false; | 238 return false; |
| 240 } | 239 } |
| 241 | 240 |
| 242 // Create new ICData, do not modify the one attached to the instruction | 241 // Create new ICData, do not modify the one attached to the instruction |
| (...skipping 4059 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4302 dst_name, | 4301 dst_name, |
| 4303 call->deopt_id()); | 4302 call->deopt_id()); |
| 4304 ReplaceCall(call, assert_as); | 4303 ReplaceCall(call, assert_as); |
| 4305 } | 4304 } |
| 4306 | 4305 |
| 4307 | 4306 |
| 4308 // Special optimizations when running in --noopt mode. | 4307 // Special optimizations when running in --noopt mode. |
| 4309 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { | 4308 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { |
| 4310 // TODO(srdjan): Investigate other attempts, as they are not allowed to | 4309 // TODO(srdjan): Investigate other attempts, as they are not allowed to |
| 4311 // deoptimize. | 4310 // deoptimize. |
| 4312 const Token::Kind op_kind = instr->token_kind(); | |
| 4313 if ((op_kind == Token::kGET) && | |
| 4314 TryInlineInstanceGetter(instr, false /* no checks allowed */)) { | |
| 4315 return; | |
| 4316 } | |
| 4317 const ICData& unary_checks = | |
| 4318 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | |
| 4319 if ((instr->ic_data()->NumberOfChecks() > 0) && | |
| 4320 (op_kind == Token::kSET) && | |
| 4321 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) { | |
| 4322 return; | |
| 4323 } | |
| 4324 if (instr->HasICData() && (instr->ic_data()->NumberOfUsedChecks() > 0)) { | |
| 4325 ASSERT(!FLAG_polymorphic_with_deopt); | |
| 4326 // OK to use checks with PolymorphicInstanceCallInstr since no | |
| 4327 // deoptimization is allowed. | |
| 4328 PolymorphicInstanceCallInstr* call = | |
| 4329 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
| 4330 true /* call_with_checks */); | |
| 4331 instr->ReplaceWith(call, current_iterator()); | |
| 4332 return; | |
| 4333 } | |
| 4334 | 4311 |
| 4335 // Type test is special as it always gets converted into inlined code. | 4312 // Type test is special as it always gets converted into inlined code. |
| 4313 const Token::Kind op_kind = instr->token_kind(); | |
| 4336 if (Token::IsTypeTestOperator(op_kind)) { | 4314 if (Token::IsTypeTestOperator(op_kind)) { |
| 4337 ReplaceWithInstanceOf(instr); | 4315 ReplaceWithInstanceOf(instr); |
| 4338 return; | 4316 return; |
| 4339 } | 4317 } |
| 4340 if (Token::IsTypeCastOperator(op_kind)) { | 4318 if (Token::IsTypeCastOperator(op_kind)) { |
| 4341 ReplaceWithTypeCast(instr); | 4319 ReplaceWithTypeCast(instr); |
| 4342 return; | 4320 return; |
| 4343 } | 4321 } |
| 4322 | |
| 4323 if ((op_kind == Token::kGET) && | |
| 4324 TryInlineInstanceGetter(instr, false /* no checks allowed */)) { | |
| 4325 return; | |
| 4326 } | |
| 4327 const ICData& unary_checks = | |
| 4328 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | |
| 4329 if ((unary_checks.NumberOfChecks() > 0) && | |
| 4330 (op_kind == Token::kSET) && | |
| 4331 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) { | |
| 4332 return; | |
| 4333 } | |
| 4334 | |
| 4335 bool has_one_target = | |
| 4336 (unary_checks.NumberOfChecks() > 0) && unary_checks.HasOneTarget(); | |
| 4337 if (has_one_target) { | |
| 4338 // Check if the single target is a polymorphic target, if it is, | |
| 4339 // we don't have one target. | |
| 4340 const Function& target = | |
| 4341 Function::Handle(Z, unary_checks.GetTargetAt(0)); | |
| 4342 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); | |
| 4343 has_one_target = !polymorphic_target; | |
| 4344 } | |
| 4345 | |
| 4346 if (has_one_target) { | |
| 4347 RawFunction::Kind function_kind = | |
| 4348 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); | |
| 4349 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { | |
| 4350 PolymorphicInstanceCallInstr* call = | |
| 4351 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
| 4352 /* with_checks = */ false); | |
| 4353 instr->ReplaceWith(call, current_iterator()); | |
| 4354 return; | |
| 4355 } | |
| 4356 } | |
| 4357 | |
| 4358 // More than one targets. Generate generic polymorphic call without | |
| 4359 // deoptimization. | |
| 4360 if (instr->ic_data()->NumberOfUsedChecks() > 0) { | |
| 4361 ASSERT(!FLAG_polymorphic_with_deopt); | |
| 4362 // OK to use checks with PolymorphicInstanceCallInstr since no | |
| 4363 // deoptimization is allowed. | |
| 4364 PolymorphicInstanceCallInstr* call = | |
| 4365 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
| 4366 /* with_checks = */ true); | |
| 4367 instr->ReplaceWith(call, current_iterator()); | |
| 4368 return; | |
| 4369 } | |
| 4370 | |
| 4371 // No IC data checks. Try resolve target using the propagated type. | |
| 4372 // If the propagated type has a method with the target name and there are | |
| 4373 // no overrides with that name according to CHA, call the method directly. | |
| 4374 const AbstractType* receiver_type = | |
| 4375 instr->PushArgumentAt(0)->value()->Type()->ToAbstractType(); | |
| 4376 if (receiver_type->IsDynamicType()) return; | |
| 4377 if (receiver_type->HasResolvedTypeClass()) { | |
| 4378 const Class& receiver_class = Class::Handle(Z, | |
| 4379 receiver_type->type_class()); | |
| 4380 const Array& args_desc_array = Array::Handle(Z, | |
| 4381 ArgumentsDescriptor::New(instr->ArgumentCount(), | |
| 4382 instr->argument_names())); | |
| 4383 ArgumentsDescriptor args_desc(args_desc_array); | |
| 4384 const Function& function = Function::Handle(Z, | |
| 4385 Resolver::ResolveDynamicForReceiverClass( | |
| 4386 receiver_class, | |
| 4387 instr->function_name(), | |
| 4388 args_desc)); | |
| 4389 if (function.IsNull()) { | |
| 4390 return; | |
| 4391 } | |
| 4392 if (!thread()->cha()->HasOverride(receiver_class, instr->function_name())) { | |
| 4393 if (FLAG_trace_cha) { | |
| 4394 THR_Print(" **(CHA) Instance call needs no check, " | |
| 4395 "no overrides of '%s' '%s'\n", | |
| 4396 instr->function_name().ToCString(), receiver_class.ToCString()); | |
| 4397 } | |
| 4398 thread()->cha()->AddToLeafClasses(receiver_class); | |
| 4399 | |
| 4400 // Create fake IC data with the resolved target. | |
| 4401 const ICData& ic_data = ICData::Handle( | |
| 4402 ICData::New(flow_graph_->function(), | |
| 4403 instr->function_name(), | |
| 4404 args_desc_array, | |
| 4405 Thread::kNoDeoptId, | |
| 4406 1)); | |
|
rmacnak
2015/10/21 18:00:30
1 /* args_tested */
Florian Schneider
2015/10/22 13:59:25
Done.
| |
| 4407 ic_data.AddReceiverCheck(receiver_class.id(), function); | |
| 4408 PolymorphicInstanceCallInstr* call = | |
| 4409 new(Z) PolymorphicInstanceCallInstr(instr, ic_data, | |
| 4410 /* with_checks = */ false); | |
| 4411 instr->ReplaceWith(call, current_iterator()); | |
| 4412 return; | |
| 4413 } | |
| 4414 } | |
| 4344 } | 4415 } |
| 4345 | 4416 |
| 4346 | 4417 |
| 4347 // Tries to optimize instance call by replacing it with a faster instruction | 4418 // Tries to optimize instance call by replacing it with a faster instruction |
| 4348 // (e.g, binary op, field load, ..). | 4419 // (e.g, binary op, field load, ..). |
| 4349 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 4420 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 4350 if (Compiler::always_optimize()) { | 4421 if (Compiler::always_optimize()) { |
| 4351 InstanceCallNoopt(instr); | 4422 InstanceCallNoopt(instr); |
| 4352 return; | 4423 return; |
| 4353 } | 4424 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4424 const Function& target = | 4495 const Function& target = |
| 4425 Function::Handle(Z, unary_checks.GetTargetAt(0)); | 4496 Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 4426 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); | 4497 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); |
| 4427 has_one_target = !polymorphic_target; | 4498 has_one_target = !polymorphic_target; |
| 4428 } | 4499 } |
| 4429 | 4500 |
| 4430 if (has_one_target) { | 4501 if (has_one_target) { |
| 4431 RawFunction::Kind function_kind = | 4502 RawFunction::Kind function_kind = |
| 4432 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); | 4503 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); |
| 4433 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { | 4504 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 4434 const bool call_with_checks = false; | |
| 4435 PolymorphicInstanceCallInstr* call = | 4505 PolymorphicInstanceCallInstr* call = |
| 4436 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 4506 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 4437 call_with_checks); | 4507 /* call_with_checks = */ false); |
| 4438 instr->ReplaceWith(call, current_iterator()); | 4508 instr->ReplaceWith(call, current_iterator()); |
| 4439 return; | 4509 return; |
| 4440 } | 4510 } |
| 4441 } | 4511 } |
| 4442 | 4512 |
| 4443 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { | 4513 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { |
| 4444 bool call_with_checks; | 4514 bool call_with_checks; |
| 4445 if (has_one_target && FLAG_polymorphic_with_deopt) { | 4515 if (has_one_target && FLAG_polymorphic_with_deopt) { |
| 4446 // Type propagation has not run yet, we cannot eliminate the check. | 4516 // Type propagation has not run yet, we cannot eliminate the check. |
| 4447 AddReceiverCheck(instr); | 4517 AddReceiverCheck(instr); |
| (...skipping 4367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8815 | 8885 |
| 8816 // Insert materializations at environment uses. | 8886 // Insert materializations at environment uses. |
| 8817 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { | 8887 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { |
| 8818 CreateMaterializationAt( | 8888 CreateMaterializationAt( |
| 8819 exits_collector_.exits()[i], alloc, *slots); | 8889 exits_collector_.exits()[i], alloc, *slots); |
| 8820 } | 8890 } |
| 8821 } | 8891 } |
| 8822 | 8892 |
| 8823 | 8893 |
| 8824 } // namespace dart | 8894 } // namespace dart |
| OLD | NEW |