| 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 3960 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4203 dst_name, | 4202 dst_name, |
| 4204 call->deopt_id()); | 4203 call->deopt_id()); |
| 4205 ReplaceCall(call, assert_as); | 4204 ReplaceCall(call, assert_as); |
| 4206 } | 4205 } |
| 4207 | 4206 |
| 4208 | 4207 |
| 4209 // Special optimizations when running in --noopt mode. | 4208 // Special optimizations when running in --noopt mode. |
| 4210 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { | 4209 void FlowGraphOptimizer::InstanceCallNoopt(InstanceCallInstr* instr) { |
| 4211 // TODO(srdjan): Investigate other attempts, as they are not allowed to | 4210 // TODO(srdjan): Investigate other attempts, as they are not allowed to |
| 4212 // deoptimize. | 4211 // deoptimize. |
| 4213 const Token::Kind op_kind = instr->token_kind(); | |
| 4214 if ((op_kind == Token::kGET) && | |
| 4215 TryInlineInstanceGetter(instr, false /* no checks allowed */)) { | |
| 4216 return; | |
| 4217 } | |
| 4218 const ICData& unary_checks = | |
| 4219 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | |
| 4220 if ((instr->ic_data()->NumberOfChecks() > 0) && | |
| 4221 (op_kind == Token::kSET) && | |
| 4222 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) { | |
| 4223 return; | |
| 4224 } | |
| 4225 if (instr->HasICData() && (instr->ic_data()->NumberOfUsedChecks() > 0)) { | |
| 4226 ASSERT(!FLAG_polymorphic_with_deopt); | |
| 4227 // OK to use checks with PolymorphicInstanceCallInstr since no | |
| 4228 // deoptimization is allowed. | |
| 4229 PolymorphicInstanceCallInstr* call = | |
| 4230 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | |
| 4231 true /* call_with_checks */); | |
| 4232 instr->ReplaceWith(call, current_iterator()); | |
| 4233 return; | |
| 4234 } | |
| 4235 | 4212 |
| 4236 // Type test is special as it always gets converted into inlined code. | 4213 // Type test is special as it always gets converted into inlined code. |
| 4214 const Token::Kind op_kind = instr->token_kind(); |
| 4237 if (Token::IsTypeTestOperator(op_kind)) { | 4215 if (Token::IsTypeTestOperator(op_kind)) { |
| 4238 ReplaceWithInstanceOf(instr); | 4216 ReplaceWithInstanceOf(instr); |
| 4239 return; | 4217 return; |
| 4240 } | 4218 } |
| 4241 if (Token::IsTypeCastOperator(op_kind)) { | 4219 if (Token::IsTypeCastOperator(op_kind)) { |
| 4242 ReplaceWithTypeCast(instr); | 4220 ReplaceWithTypeCast(instr); |
| 4243 return; | 4221 return; |
| 4244 } | 4222 } |
| 4223 |
| 4224 if ((op_kind == Token::kGET) && |
| 4225 TryInlineInstanceGetter(instr, false /* no checks allowed */)) { |
| 4226 return; |
| 4227 } |
| 4228 const ICData& unary_checks = |
| 4229 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
| 4230 if ((unary_checks.NumberOfChecks() > 0) && |
| 4231 (op_kind == Token::kSET) && |
| 4232 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) { |
| 4233 return; |
| 4234 } |
| 4235 |
| 4236 bool has_one_target = |
| 4237 (unary_checks.NumberOfChecks() > 0) && unary_checks.HasOneTarget(); |
| 4238 if (has_one_target) { |
| 4239 // Check if the single target is a polymorphic target, if it is, |
| 4240 // we don't have one target. |
| 4241 const Function& target = |
| 4242 Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 4243 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); |
| 4244 has_one_target = !polymorphic_target; |
| 4245 } |
| 4246 |
| 4247 if (has_one_target) { |
| 4248 RawFunction::Kind function_kind = |
| 4249 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); |
| 4250 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 4251 PolymorphicInstanceCallInstr* call = |
| 4252 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 4253 /* with_checks = */ false); |
| 4254 instr->ReplaceWith(call, current_iterator()); |
| 4255 return; |
| 4256 } |
| 4257 } |
| 4258 |
| 4259 // More than one targets. Generate generic polymorphic call without |
| 4260 // deoptimization. |
| 4261 if (instr->ic_data()->NumberOfUsedChecks() > 0) { |
| 4262 ASSERT(!FLAG_polymorphic_with_deopt); |
| 4263 // OK to use checks with PolymorphicInstanceCallInstr since no |
| 4264 // deoptimization is allowed. |
| 4265 PolymorphicInstanceCallInstr* call = |
| 4266 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 4267 /* with_checks = */ true); |
| 4268 instr->ReplaceWith(call, current_iterator()); |
| 4269 return; |
| 4270 } |
| 4271 |
| 4272 // No IC data checks. Try resolve target using the propagated type. |
| 4273 // If the propagated type has a method with the target name and there are |
| 4274 // no overrides with that name according to CHA, call the method directly. |
| 4275 const AbstractType* receiver_type = |
| 4276 instr->PushArgumentAt(0)->value()->Type()->ToAbstractType(); |
| 4277 if (receiver_type->IsDynamicType()) return; |
| 4278 if (receiver_type->HasResolvedTypeClass()) { |
| 4279 const Class& receiver_class = Class::Handle(Z, |
| 4280 receiver_type->type_class()); |
| 4281 const Array& args_desc_array = Array::Handle(Z, |
| 4282 ArgumentsDescriptor::New(instr->ArgumentCount(), |
| 4283 instr->argument_names())); |
| 4284 ArgumentsDescriptor args_desc(args_desc_array); |
| 4285 const Function& function = Function::Handle(Z, |
| 4286 Resolver::ResolveDynamicForReceiverClass( |
| 4287 receiver_class, |
| 4288 instr->function_name(), |
| 4289 args_desc)); |
| 4290 if (function.IsNull()) { |
| 4291 return; |
| 4292 } |
| 4293 if (!thread()->cha()->HasOverride(receiver_class, instr->function_name())) { |
| 4294 if (FLAG_trace_cha) { |
| 4295 THR_Print(" **(CHA) Instance call needs no check, " |
| 4296 "no overrides of '%s' '%s'\n", |
| 4297 instr->function_name().ToCString(), receiver_class.ToCString()); |
| 4298 } |
| 4299 thread()->cha()->AddToLeafClasses(receiver_class); |
| 4300 |
| 4301 // Create fake IC data with the resolved target. |
| 4302 const ICData& ic_data = ICData::Handle( |
| 4303 ICData::New(flow_graph_->function(), |
| 4304 instr->function_name(), |
| 4305 args_desc_array, |
| 4306 Thread::kNoDeoptId, |
| 4307 /* args_tested = */ 1)); |
| 4308 ic_data.AddReceiverCheck(receiver_class.id(), function); |
| 4309 PolymorphicInstanceCallInstr* call = |
| 4310 new(Z) PolymorphicInstanceCallInstr(instr, ic_data, |
| 4311 /* with_checks = */ false); |
| 4312 instr->ReplaceWith(call, current_iterator()); |
| 4313 return; |
| 4314 } |
| 4315 } |
| 4245 } | 4316 } |
| 4246 | 4317 |
| 4247 | 4318 |
| 4248 // Tries to optimize instance call by replacing it with a faster instruction | 4319 // Tries to optimize instance call by replacing it with a faster instruction |
| 4249 // (e.g, binary op, field load, ..). | 4320 // (e.g, binary op, field load, ..). |
| 4250 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 4321 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 4251 if (Compiler::always_optimize()) { | 4322 if (Compiler::always_optimize()) { |
| 4252 InstanceCallNoopt(instr); | 4323 InstanceCallNoopt(instr); |
| 4253 return; | 4324 return; |
| 4254 } | 4325 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4325 const Function& target = | 4396 const Function& target = |
| 4326 Function::Handle(Z, unary_checks.GetTargetAt(0)); | 4397 Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 4327 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); | 4398 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); |
| 4328 has_one_target = !polymorphic_target; | 4399 has_one_target = !polymorphic_target; |
| 4329 } | 4400 } |
| 4330 | 4401 |
| 4331 if (has_one_target) { | 4402 if (has_one_target) { |
| 4332 RawFunction::Kind function_kind = | 4403 RawFunction::Kind function_kind = |
| 4333 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); | 4404 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); |
| 4334 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { | 4405 if (!InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 4335 const bool call_with_checks = false; | |
| 4336 PolymorphicInstanceCallInstr* call = | 4406 PolymorphicInstanceCallInstr* call = |
| 4337 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 4407 new(Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 4338 call_with_checks); | 4408 /* call_with_checks = */ false); |
| 4339 instr->ReplaceWith(call, current_iterator()); | 4409 instr->ReplaceWith(call, current_iterator()); |
| 4340 return; | 4410 return; |
| 4341 } | 4411 } |
| 4342 } | 4412 } |
| 4343 | 4413 |
| 4344 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { | 4414 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { |
| 4345 bool call_with_checks; | 4415 bool call_with_checks; |
| 4346 if (has_one_target && FLAG_polymorphic_with_deopt) { | 4416 if (has_one_target && FLAG_polymorphic_with_deopt) { |
| 4347 // Type propagation has not run yet, we cannot eliminate the check. | 4417 // Type propagation has not run yet, we cannot eliminate the check. |
| 4348 AddReceiverCheck(instr); | 4418 AddReceiverCheck(instr); |
| (...skipping 4367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8716 | 8786 |
| 8717 // Insert materializations at environment uses. | 8787 // Insert materializations at environment uses. |
| 8718 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { | 8788 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { |
| 8719 CreateMaterializationAt( | 8789 CreateMaterializationAt( |
| 8720 exits_collector_.exits()[i], alloc, *slots); | 8790 exits_collector_.exits()[i], alloc, *slots); |
| 8721 } | 8791 } |
| 8722 } | 8792 } |
| 8723 | 8793 |
| 8724 | 8794 |
| 8725 } // namespace dart | 8795 } // namespace dart |
| OLD | NEW |