| 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/dart_entry.h" | 9 #include "vm/dart_entry.h" |
| 10 #include "vm/flow_graph_builder.h" | 10 #include "vm/flow_graph_builder.h" |
| 11 #include "vm/flow_graph_compiler.h" | 11 #include "vm/flow_graph_compiler.h" |
| 12 #include "vm/hash_map.h" | 12 #include "vm/hash_map.h" |
| 13 #include "vm/il_printer.h" | 13 #include "vm/il_printer.h" |
| 14 #include "vm/intermediate_language.h" | 14 #include "vm/intermediate_language.h" |
| 15 #include "vm/object_store.h" | 15 #include "vm/object_store.h" |
| 16 #include "vm/parser.h" | 16 #include "vm/parser.h" |
| 17 #include "vm/resolver.h" | 17 #include "vm/resolver.h" |
| 18 #include "vm/scopes.h" | 18 #include "vm/scopes.h" |
| 19 #include "vm/stack_frame.h" | 19 #include "vm/stack_frame.h" |
| 20 #include "vm/symbols.h" | 20 #include "vm/symbols.h" |
| 21 | 21 |
| 22 namespace dart { | 22 namespace dart { |
| 23 | 23 |
| 24 DEFINE_FLAG(bool, array_bounds_check_elimination, true, | 24 DEFINE_FLAG(bool, array_bounds_check_elimination, true, |
| 25 "Eliminate redundant bounds checks."); | 25 "Eliminate redundant bounds checks."); |
| 26 DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination."); | 26 DEFINE_FLAG(bool, load_cse, true, "Use redundant load elimination."); |
| 27 DEFINE_FLAG(int, max_polymorphic_checks, 4, | 27 DEFINE_FLAG(int, max_polymorphic_checks, 4, |
| 28 "Maximum number of polymorphic check, otherwise it is megamorphic."); | 28 "Maximum number of polymorphic check, otherwise it is megamorphic."); |
| 29 DEFINE_FLAG(int, max_equality_polymorphic_checks, 32, |
| 30 "Maximum number of polymorphic checks in equality operator," |
| 31 " otherwise use megamorphic dispatch."); |
| 29 DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis."); | 32 DEFINE_FLAG(bool, remove_redundant_phis, true, "Remove redundant phis."); |
| 30 DEFINE_FLAG(bool, trace_constant_propagation, false, | 33 DEFINE_FLAG(bool, trace_constant_propagation, false, |
| 31 "Print constant propagation and useless code elimination."); | 34 "Print constant propagation and useless code elimination."); |
| 32 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); | 35 DEFINE_FLAG(bool, trace_optimization, false, "Print optimization details."); |
| 33 DEFINE_FLAG(bool, trace_range_analysis, false, "Trace range analysis progress"); | 36 DEFINE_FLAG(bool, trace_range_analysis, false, "Trace range analysis progress"); |
| 34 DEFINE_FLAG(bool, truncating_left_shift, true, | 37 DEFINE_FLAG(bool, truncating_left_shift, true, |
| 35 "Optimize left shift to truncate if possible"); | 38 "Optimize left shift to truncate if possible"); |
| 36 DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis."); | 39 DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis."); |
| 37 DEFINE_FLAG(bool, trace_load_optimization, false, | 40 DEFINE_FLAG(bool, trace_load_optimization, false, |
| 38 "Print live sets for load optimization pass."); | 41 "Print live sets for load optimization pass."); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 } | 81 } |
| 79 } | 82 } |
| 80 } else if (instr->IsPolymorphicInstanceCall()) { | 83 } else if (instr->IsPolymorphicInstanceCall()) { |
| 81 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall()); | 84 SpecializePolymorphicInstanceCall(instr->AsPolymorphicInstanceCall()); |
| 82 } else if (instr->IsStrictCompare()) { | 85 } else if (instr->IsStrictCompare()) { |
| 83 VisitStrictCompare(instr->AsStrictCompare()); | 86 VisitStrictCompare(instr->AsStrictCompare()); |
| 84 } else if (instr->IsBranch()) { | 87 } else if (instr->IsBranch()) { |
| 85 ComparisonInstr* compare = instr->AsBranch()->comparison(); | 88 ComparisonInstr* compare = instr->AsBranch()->comparison(); |
| 86 if (compare->IsStrictCompare()) { | 89 if (compare->IsStrictCompare()) { |
| 87 VisitStrictCompare(compare->AsStrictCompare()); | 90 VisitStrictCompare(compare->AsStrictCompare()); |
| 88 } else if (compare->IsEqualityCompare()) { | |
| 89 StrictifyEqualityCompare(compare->AsEqualityCompare(), | |
| 90 instr->AsBranch()); | |
| 91 } | 91 } |
| 92 } | 92 } |
| 93 } | 93 } |
| 94 current_iterator_ = NULL; | 94 current_iterator_ = NULL; |
| 95 } | 95 } |
| 96 } | 96 } |
| 97 | 97 |
| 98 | 98 |
| 99 // Attempt to build ICData for call using propagated class-ids. | 99 // Attempt to build ICData for call using propagated class-ids. |
| 100 bool FlowGraphOptimizer::TryCreateICData(InstanceCallInstr* call) { | 100 bool FlowGraphOptimizer::TryCreateICData(InstanceCallInstr* call) { |
| (...skipping 1180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1281 ASSERT(current_iterator()->Current() == call); | 1281 ASSERT(current_iterator()->Current() == call); |
| 1282 current_iterator()->RemoveCurrentFromGraph(); | 1282 current_iterator()->RemoveCurrentFromGraph(); |
| 1283 call->set_previous(NULL); | 1283 call->set_previous(NULL); |
| 1284 call->set_next(NULL); | 1284 call->set_next(NULL); |
| 1285 return true; | 1285 return true; |
| 1286 } | 1286 } |
| 1287 | 1287 |
| 1288 | 1288 |
| 1289 static bool SmiFitsInDouble() { return kSmiBits < 53; } | 1289 static bool SmiFitsInDouble() { return kSmiBits < 53; } |
| 1290 | 1290 |
| 1291 bool FlowGraphOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
| 1292 Token::Kind op_kind) { |
| 1293 const ICData& ic_data = *call->ic_data(); |
| 1294 ASSERT(ic_data.num_args_tested() == 2); |
| 1295 |
| 1296 ASSERT(call->ArgumentCount() == 2); |
| 1297 Definition* left = call->ArgumentAt(0); |
| 1298 Definition* right = call->ArgumentAt(1); |
| 1299 |
| 1300 intptr_t cid = kIllegalCid; |
| 1301 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 1302 InsertBefore(call, |
| 1303 new CheckSmiInstr(new Value(left), call->deopt_id()), |
| 1304 call->env(), |
| 1305 Definition::kEffect); |
| 1306 InsertBefore(call, |
| 1307 new CheckSmiInstr(new Value(right), call->deopt_id()), |
| 1308 call->env(), |
| 1309 Definition::kEffect); |
| 1310 cid = kSmiCid; |
| 1311 } else if (HasTwoMintOrSmi(ic_data) && |
| 1312 FlowGraphCompiler::SupportsUnboxedMints()) { |
| 1313 cid = kMintCid; |
| 1314 } else if (HasTwoDoubleOrSmi(ic_data)) { |
| 1315 // Use double comparison. |
| 1316 if (SmiFitsInDouble()) { |
| 1317 cid = kDoubleCid; |
| 1318 } else { |
| 1319 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
| 1320 // We cannot use double comparison on two smis. Need polymorphic |
| 1321 // call. |
| 1322 return false; |
| 1323 } else { |
| 1324 InsertBefore(call, |
| 1325 new CheckEitherNonSmiInstr(new Value(left), |
| 1326 new Value(right), |
| 1327 call->deopt_id()), |
| 1328 call->env(), |
| 1329 Definition::kEffect); |
| 1330 cid = kDoubleCid; |
| 1331 } |
| 1332 } |
| 1333 } else { |
| 1334 // Check if ICDData contains checks with Smi/Null combinations. In that case |
| 1335 // we can still emit the optimized Smi equality operation but need to add |
| 1336 // checks for null or Smi. |
| 1337 GrowableArray<intptr_t> smi_or_null(2); |
| 1338 smi_or_null.Add(kSmiCid); |
| 1339 smi_or_null.Add(kNullCid); |
| 1340 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data, |
| 1341 smi_or_null, |
| 1342 smi_or_null)) { |
| 1343 const ICData& unary_checks_0 = |
| 1344 ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks()); |
| 1345 AddCheckClass(left, |
| 1346 unary_checks_0, |
| 1347 call->deopt_id(), |
| 1348 call->env(), |
| 1349 call); |
| 1350 |
| 1351 const ICData& unary_checks_1 = |
| 1352 ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecksForArgNr(1)); |
| 1353 AddCheckClass(right, |
| 1354 unary_checks_1, |
| 1355 call->deopt_id(), |
| 1356 call->env(), |
| 1357 call); |
| 1358 cid = kSmiCid; |
| 1359 } else { |
| 1360 // Shortcut for equality with null. |
| 1361 ConstantInstr* right_const = right->AsConstant(); |
| 1362 ConstantInstr* left_const = left->AsConstant(); |
| 1363 if ((right_const != NULL && right_const->value().IsNull()) || |
| 1364 (left_const != NULL && left_const->value().IsNull())) { |
| 1365 StrictCompareInstr* comp = new StrictCompareInstr(call->token_pos(), |
| 1366 Token::kEQ_STRICT, |
| 1367 new Value(left), |
| 1368 new Value(right)); |
| 1369 ReplaceCall(call, comp); |
| 1370 return true; |
| 1371 } |
| 1372 return false; |
| 1373 } |
| 1374 } |
| 1375 ASSERT(cid != kIllegalCid); |
| 1376 EqualityCompareInstr* comp = new EqualityCompareInstr(call->token_pos(), |
| 1377 op_kind, |
| 1378 new Value(left), |
| 1379 new Value(right), |
| 1380 cid, |
| 1381 call->deopt_id()); |
| 1382 ReplaceCall(call, comp); |
| 1383 return true; |
| 1384 } |
| 1385 |
| 1291 | 1386 |
| 1292 bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, | 1387 bool FlowGraphOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, |
| 1293 Token::Kind op_kind) { | 1388 Token::Kind op_kind) { |
| 1294 const ICData& ic_data = *call->ic_data(); | 1389 const ICData& ic_data = *call->ic_data(); |
| 1295 ASSERT(ic_data.num_args_tested() == 2); | 1390 ASSERT(ic_data.num_args_tested() == 2); |
| 1296 | 1391 |
| 1297 ASSERT(call->ArgumentCount() == 2); | 1392 ASSERT(call->ArgumentCount() == 2); |
| 1298 Definition* left = call->ArgumentAt(0); | 1393 Definition* left = call->ArgumentAt(0); |
| 1299 Definition* right = call->ArgumentAt(1); | 1394 Definition* right = call->ArgumentAt(1); |
| 1300 | 1395 |
| (...skipping 1625 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2926 if (prev.IsNull()) { | 3021 if (prev.IsNull()) { |
| 2927 prev = Bool::Get(is_subtype).raw(); | 3022 prev = Bool::Get(is_subtype).raw(); |
| 2928 } else { | 3023 } else { |
| 2929 if (is_subtype != prev.value()) return Bool::null(); | 3024 if (is_subtype != prev.value()) return Bool::null(); |
| 2930 } | 3025 } |
| 2931 } | 3026 } |
| 2932 return prev.raw(); | 3027 return prev.raw(); |
| 2933 } | 3028 } |
| 2934 | 3029 |
| 2935 | 3030 |
| 3031 static Definition* OriginalDefinition(Definition* defn) { |
| 3032 while (defn->IsRedefinition() || defn->IsAssertAssignable()) { |
| 3033 if (defn->IsRedefinition()) { |
| 3034 defn = defn->AsRedefinition()->value()->definition(); |
| 3035 } else { |
| 3036 defn = defn->AsAssertAssignable()->value()->definition(); |
| 3037 } |
| 3038 } |
| 3039 return defn; |
| 3040 } |
| 3041 |
| 3042 |
| 2936 // TODO(srdjan): Use ICData to check if always true or false. | 3043 // TODO(srdjan): Use ICData to check if always true or false. |
| 2937 void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { | 3044 void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) { |
| 2938 ASSERT(Token::IsTypeTestOperator(call->token_kind())); | 3045 ASSERT(Token::IsTypeTestOperator(call->token_kind())); |
| 2939 Definition* left = call->ArgumentAt(0); | 3046 Definition* left = call->ArgumentAt(0); |
| 2940 Definition* instantiator = call->ArgumentAt(1); | 3047 Definition* instantiator = call->ArgumentAt(1); |
| 2941 Definition* type_args = call->ArgumentAt(2); | 3048 Definition* type_args = call->ArgumentAt(2); |
| 2942 const AbstractType& type = | 3049 const AbstractType& type = |
| 2943 AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value()); | 3050 AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value()); |
| 2944 const bool negate = | 3051 const bool negate = Bool::Cast( |
| 2945 Bool::Cast(call->ArgumentAt(4)->AsConstant()->value()).value(); | 3052 OriginalDefinition(call->ArgumentAt(4))->AsConstant()->value()).value(); |
| 2946 const ICData& unary_checks = | 3053 const ICData& unary_checks = |
| 2947 ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks()); | 3054 ICData::ZoneHandle(call->ic_data()->AsUnaryClassChecks()); |
| 2948 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { | 3055 if (unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks) { |
| 2949 Bool& as_bool = Bool::ZoneHandle(InstanceOfAsBool(unary_checks, type)); | 3056 Bool& as_bool = Bool::ZoneHandle(InstanceOfAsBool(unary_checks, type)); |
| 2950 if (!as_bool.IsNull()) { | 3057 if (!as_bool.IsNull()) { |
| 2951 AddReceiverCheck(call); | 3058 AddReceiverCheck(call); |
| 2952 if (negate) { | 3059 if (negate) { |
| 2953 as_bool = Bool::Get(!as_bool.value()).raw(); | 3060 as_bool = Bool::Get(!as_bool.value()).raw(); |
| 2954 } | 3061 } |
| 2955 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool); | 3062 ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3034 } | 3141 } |
| 3035 | 3142 |
| 3036 if (Token::IsTypeCastOperator(op_kind)) { | 3143 if (Token::IsTypeCastOperator(op_kind)) { |
| 3037 ReplaceWithTypeCast(instr); | 3144 ReplaceWithTypeCast(instr); |
| 3038 return; | 3145 return; |
| 3039 } | 3146 } |
| 3040 | 3147 |
| 3041 const ICData& unary_checks = | 3148 const ICData& unary_checks = |
| 3042 ICData::ZoneHandle(instr->ic_data()->AsUnaryClassChecks()); | 3149 ICData::ZoneHandle(instr->ic_data()->AsUnaryClassChecks()); |
| 3043 | 3150 |
| 3044 if ((unary_checks.NumberOfChecks() > FLAG_max_polymorphic_checks) && | 3151 intptr_t max_checks = (op_kind == Token::kEQ) |
| 3152 ? FLAG_max_equality_polymorphic_checks |
| 3153 : FLAG_max_polymorphic_checks; |
| 3154 if ((unary_checks.NumberOfChecks() > max_checks) && |
| 3045 InstanceCallNeedsClassCheck(instr)) { | 3155 InstanceCallNeedsClassCheck(instr)) { |
| 3046 // Too many checks, it will be megamorphic which needs unary checks. | 3156 // Too many checks, it will be megamorphic which needs unary checks. |
| 3047 instr->set_ic_data(&unary_checks); | 3157 instr->set_ic_data(&unary_checks); |
| 3048 return; | 3158 return; |
| 3049 } | 3159 } |
| 3050 | 3160 |
| 3051 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithStoreIndexed(instr)) { | 3161 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithStoreIndexed(instr)) { |
| 3052 return; | 3162 return; |
| 3053 } | 3163 } |
| 3054 if ((op_kind == Token::kINDEX) && TryReplaceWithLoadIndexed(instr)) { | 3164 if ((op_kind == Token::kINDEX) && TryReplaceWithLoadIndexed(instr)) { |
| 3055 return; | 3165 return; |
| 3056 } | 3166 } |
| 3057 | 3167 |
| 3168 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { |
| 3169 return; |
| 3170 } |
| 3171 |
| 3058 if (Token::IsRelationalOperator(op_kind) && | 3172 if (Token::IsRelationalOperator(op_kind) && |
| 3059 TryReplaceWithRelationalOp(instr, op_kind)) { | 3173 TryReplaceWithRelationalOp(instr, op_kind)) { |
| 3060 return; | 3174 return; |
| 3061 } | 3175 } |
| 3062 | 3176 |
| 3063 if (Token::IsBinaryOperator(op_kind) && | 3177 if (Token::IsBinaryOperator(op_kind) && |
| 3064 TryReplaceWithBinaryOp(instr, op_kind)) { | 3178 TryReplaceWithBinaryOp(instr, op_kind)) { |
| 3065 return; | 3179 return; |
| 3066 } | 3180 } |
| 3067 if (Token::IsPrefixOperator(op_kind) && | 3181 if (Token::IsPrefixOperator(op_kind) && |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3273 new Value(instr->ArgumentAt(1)), | 3387 new Value(instr->ArgumentAt(1)), |
| 3274 needs_store_barrier); | 3388 needs_store_barrier); |
| 3275 // Discard the environment from the original instruction because the store | 3389 // Discard the environment from the original instruction because the store |
| 3276 // can't deoptimize. | 3390 // can't deoptimize. |
| 3277 instr->RemoveEnvironment(); | 3391 instr->RemoveEnvironment(); |
| 3278 ReplaceCall(instr, store); | 3392 ReplaceCall(instr, store); |
| 3279 return true; | 3393 return true; |
| 3280 } | 3394 } |
| 3281 | 3395 |
| 3282 | 3396 |
| 3283 bool FlowGraphOptimizer::CanStrictifyEqualityCompare( | |
| 3284 EqualityCompareInstr* compare) { | |
| 3285 // If one of the inputs is null this is a strict comparison. | |
| 3286 if (compare->left()->BindsToConstantNull() || | |
| 3287 compare->right()->BindsToConstantNull()) { | |
| 3288 return true; | |
| 3289 } | |
| 3290 | |
| 3291 if (compare->left()->Type()->IsNone()) { | |
| 3292 return false; // We might be running prior to any type propagation passes. | |
| 3293 } | |
| 3294 | |
| 3295 // Try resolving target function using propagated cid for the receiver. | |
| 3296 // If receiver is either null or has default equality operator then | |
| 3297 // we can convert such comparison to a strict one. | |
| 3298 const intptr_t receiver_cid = | |
| 3299 compare->left()->Type()->ToNullableCid(); | |
| 3300 | |
| 3301 if (receiver_cid == kDynamicCid) { | |
| 3302 return false; | |
| 3303 } | |
| 3304 | |
| 3305 const Class& receiver_class = Class::Handle( | |
| 3306 Isolate::Current()->class_table()->At(receiver_cid)); | |
| 3307 | |
| 3308 // Resolve equality operator. | |
| 3309 const intptr_t kNumArgs = 2; | |
| 3310 ArgumentsDescriptor args_desc( | |
| 3311 Array::Handle(ArgumentsDescriptor::New(kNumArgs))); | |
| 3312 const Function& function = Function::Handle( | |
| 3313 Resolver::ResolveDynamicForReceiverClass( | |
| 3314 receiver_class, | |
| 3315 Symbols::EqualOperator(), | |
| 3316 args_desc)); | |
| 3317 | |
| 3318 if (function.IsNull()) { | |
| 3319 return false; | |
| 3320 } | |
| 3321 | |
| 3322 // Default equality operator declared on the Object class just calls | |
| 3323 // identical. | |
| 3324 return (Class::Handle(function.Owner()).id() == kInstanceCid); | |
| 3325 } | |
| 3326 | |
| 3327 | |
| 3328 template <typename T> | |
| 3329 bool FlowGraphOptimizer::StrictifyEqualityCompare( | |
| 3330 EqualityCompareInstr* compare, | |
| 3331 T current_instruction) const { | |
| 3332 if (CanStrictifyEqualityCompare(compare)) { | |
| 3333 Token::Kind strict_kind = (compare->kind() == Token::kEQ) ? | |
| 3334 Token::kEQ_STRICT : Token::kNE_STRICT; | |
| 3335 StrictCompareInstr* strict_comp = | |
| 3336 new StrictCompareInstr(compare->token_pos(), | |
| 3337 strict_kind, | |
| 3338 compare->left()->CopyWithType(), | |
| 3339 compare->right()->CopyWithType()); | |
| 3340 // Numbers override equality and are therefore not part of this conversion. | |
| 3341 strict_comp->set_needs_number_check(false); | |
| 3342 current_instruction->ReplaceWith(strict_comp, current_iterator()); | |
| 3343 return true; | |
| 3344 } | |
| 3345 return false; | |
| 3346 } | |
| 3347 | |
| 3348 | |
| 3349 // Returns true if we converted EqualityCompare to StrictCompare. | |
| 3350 template <typename T> | |
| 3351 bool FlowGraphOptimizer::StrictifyEqualityCompareWithICData( | |
| 3352 EqualityCompareInstr* compare, | |
| 3353 const ICData& unary_ic_data, | |
| 3354 T current_instruction) { | |
| 3355 ASSERT(unary_ic_data.num_args_tested() == 1); | |
| 3356 if (unary_ic_data.NumberOfChecks() <= FLAG_max_polymorphic_checks) { | |
| 3357 // If possible classes do not override Object's equality then replace | |
| 3358 // with strict equality. | |
| 3359 Function& target = Function::Handle(); | |
| 3360 Class& targets_class = Class::Handle(); | |
| 3361 for (intptr_t i = 0; i < unary_ic_data.NumberOfChecks(); i++) { | |
| 3362 intptr_t cid = kIllegalCid; | |
| 3363 unary_ic_data.GetOneClassCheckAt(i, &cid, &target); | |
| 3364 targets_class = target.Owner(); | |
| 3365 if (targets_class.id() != kInstanceCid) { | |
| 3366 // Overriden equality operator. | |
| 3367 return false; | |
| 3368 } | |
| 3369 } | |
| 3370 AddCheckClass(compare->left()->definition(), | |
| 3371 unary_ic_data, | |
| 3372 compare->deopt_id(), | |
| 3373 current_instruction->env(), | |
| 3374 current_instruction); | |
| 3375 ASSERT((compare->kind() == Token::kEQ) || (compare->kind() == Token::kNE)); | |
| 3376 Token::Kind strict_kind = (compare->kind() == Token::kEQ) ? | |
| 3377 Token::kEQ_STRICT : Token::kNE_STRICT; | |
| 3378 StrictCompareInstr* strict_comp = | |
| 3379 new StrictCompareInstr(compare->token_pos(), | |
| 3380 strict_kind, | |
| 3381 compare->left()->Copy(), | |
| 3382 compare->right()->Copy()); | |
| 3383 // Numbers override equality and are therefore not part of this conversion. | |
| 3384 strict_comp->set_needs_number_check(false); | |
| 3385 current_instruction->ReplaceWith(strict_comp, current_iterator()); | |
| 3386 return true; | |
| 3387 } | |
| 3388 return false; | |
| 3389 } | |
| 3390 | |
| 3391 | |
| 3392 template <typename T> | |
| 3393 void FlowGraphOptimizer::HandleEqualityCompare(EqualityCompareInstr* comp, | |
| 3394 T current_instruction) { | |
| 3395 if (StrictifyEqualityCompare(comp, current_instruction)) { | |
| 3396 // Based on input types, equality converted to strict-equality. | |
| 3397 return; | |
| 3398 } | |
| 3399 | |
| 3400 if (!comp->HasICData() || (comp->ic_data()->NumberOfChecks() == 0)) { | |
| 3401 return; | |
| 3402 } | |
| 3403 | |
| 3404 const ICData& ic_data = *comp->ic_data(); | |
| 3405 ASSERT(ic_data.num_args_tested() == 2); | |
| 3406 ASSERT(comp->operation_cid() == kIllegalCid); | |
| 3407 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | |
| 3408 InsertBefore(current_instruction, | |
| 3409 new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()), | |
| 3410 current_instruction->env(), | |
| 3411 Definition::kEffect); | |
| 3412 InsertBefore(current_instruction, | |
| 3413 new CheckSmiInstr(comp->right()->Copy(), comp->deopt_id()), | |
| 3414 current_instruction->env(), | |
| 3415 Definition::kEffect); | |
| 3416 comp->set_operation_cid(kSmiCid); | |
| 3417 } else if (HasTwoMintOrSmi(ic_data) && | |
| 3418 FlowGraphCompiler::SupportsUnboxedMints()) { | |
| 3419 comp->set_operation_cid(kMintCid); | |
| 3420 } else if (HasTwoDoubleOrSmi(ic_data)) { | |
| 3421 // Use double comparison. | |
| 3422 if (SmiFitsInDouble()) { | |
| 3423 comp->set_operation_cid(kDoubleCid); | |
| 3424 } else { | |
| 3425 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | |
| 3426 // We cannot use double comparison on two smis. | |
| 3427 ASSERT(comp->operation_cid() == kIllegalCid); | |
| 3428 } else { | |
| 3429 InsertBefore(current_instruction, | |
| 3430 new CheckEitherNonSmiInstr(comp->left()->Copy(), | |
| 3431 comp->right()->Copy(), | |
| 3432 comp->deopt_id()), | |
| 3433 current_instruction->env(), | |
| 3434 Definition::kEffect); | |
| 3435 comp->set_operation_cid(kDoubleCid); | |
| 3436 } | |
| 3437 } | |
| 3438 } | |
| 3439 | |
| 3440 if (comp->operation_cid() != kIllegalCid) { | |
| 3441 // Done. | |
| 3442 return; | |
| 3443 } | |
| 3444 | |
| 3445 const ICData& unary_checks_0 = | |
| 3446 ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecks()); | |
| 3447 if (StrictifyEqualityCompareWithICData( | |
| 3448 comp, unary_checks_0, current_instruction)) { | |
| 3449 // Based on ICData, equality converted to strict-equality. | |
| 3450 return; | |
| 3451 } | |
| 3452 | |
| 3453 // Check if ICDData contains checks with Smi/Null combinations. In that case | |
| 3454 // we can still emit the optimized Smi equality operation but need to add | |
| 3455 // checks for null or Smi. | |
| 3456 // TODO(srdjan): Add it for Double and Mint. | |
| 3457 GrowableArray<intptr_t> smi_or_null(2); | |
| 3458 smi_or_null.Add(kSmiCid); | |
| 3459 smi_or_null.Add(kNullCid); | |
| 3460 if (ICDataHasOnlyReceiverArgumentClassIds(ic_data, | |
| 3461 smi_or_null, | |
| 3462 smi_or_null)) { | |
| 3463 AddCheckClass(comp->left()->definition(), | |
| 3464 unary_checks_0, | |
| 3465 comp->deopt_id(), | |
| 3466 current_instruction->env(), | |
| 3467 current_instruction); | |
| 3468 | |
| 3469 const ICData& unary_checks_1 = | |
| 3470 ICData::ZoneHandle(comp->ic_data()->AsUnaryClassChecksForArgNr(1)); | |
| 3471 AddCheckClass(comp->right()->definition(), | |
| 3472 unary_checks_1, | |
| 3473 comp->deopt_id(), | |
| 3474 current_instruction->env(), | |
| 3475 current_instruction); | |
| 3476 comp->set_operation_cid(kSmiCid); | |
| 3477 } | |
| 3478 } | |
| 3479 | |
| 3480 | |
| 3481 | |
| 3482 | |
| 3483 void FlowGraphOptimizer::VisitEqualityCompare(EqualityCompareInstr* instr) { | |
| 3484 HandleEqualityCompare(instr, instr); | |
| 3485 } | |
| 3486 | |
| 3487 | |
| 3488 void FlowGraphOptimizer::VisitBranch(BranchInstr* instr) { | |
| 3489 ComparisonInstr* comparison = instr->comparison(); | |
| 3490 if (comparison->IsEqualityCompare()) { | |
| 3491 HandleEqualityCompare(comparison->AsEqualityCompare(), instr); | |
| 3492 } else { | |
| 3493 ASSERT(comparison->IsStrictCompare()); | |
| 3494 // Nothing to do. | |
| 3495 } | |
| 3496 } | |
| 3497 | |
| 3498 | |
| 3499 // Range analysis for smi values. | 3397 // Range analysis for smi values. |
| 3500 class RangeAnalysis : public ValueObject { | 3398 class RangeAnalysis : public ValueObject { |
| 3501 public: | 3399 public: |
| 3502 explicit RangeAnalysis(FlowGraph* flow_graph) | 3400 explicit RangeAnalysis(FlowGraph* flow_graph) |
| 3503 : flow_graph_(flow_graph), | 3401 : flow_graph_(flow_graph), |
| 3504 marked_defns_(NULL) { } | 3402 marked_defns_(NULL) { } |
| 3505 | 3403 |
| 3506 // Infer ranges for all values and remove overflow checks from binary smi | 3404 // Infer ranges for all values and remove overflow checks from binary smi |
| 3507 // operations when proven redundant. | 3405 // operations when proven redundant. |
| 3508 void Analyze(); | 3406 void Analyze(); |
| (...skipping 1085 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4594 return (kind_ == other->kind_) && | 4492 return (kind_ == other->kind_) && |
| 4595 (representation_ == other->representation_) && | 4493 (representation_ == other->representation_) && |
| 4596 (instance_ == other->instance_) && | 4494 (instance_ == other->instance_) && |
| 4597 SameField(other); | 4495 SameField(other); |
| 4598 } | 4496 } |
| 4599 | 4497 |
| 4600 // Create a zone allocated copy of this place. | 4498 // Create a zone allocated copy of this place. |
| 4601 static Place* Wrap(const Place& place); | 4499 static Place* Wrap(const Place& place); |
| 4602 | 4500 |
| 4603 private: | 4501 private: |
| 4604 static Definition* OriginalDefinition(Definition* defn) { | |
| 4605 while (defn->IsRedefinition() || defn->IsAssertAssignable()) { | |
| 4606 if (defn->IsRedefinition()) { | |
| 4607 defn = defn->AsRedefinition()->value()->definition(); | |
| 4608 } else { | |
| 4609 defn = defn->AsAssertAssignable()->value()->definition(); | |
| 4610 } | |
| 4611 } | |
| 4612 return defn; | |
| 4613 } | |
| 4614 | |
| 4615 bool SameField(Place* other) const { | 4502 bool SameField(Place* other) const { |
| 4616 return (kind_ == kField) ? (field().raw() == other->field().raw()) | 4503 return (kind_ == kField) ? (field().raw() == other->field().raw()) |
| 4617 : (offset_in_bytes_ == other->offset_in_bytes_); | 4504 : (offset_in_bytes_ == other->offset_in_bytes_); |
| 4618 } | 4505 } |
| 4619 | 4506 |
| 4620 intptr_t FieldHashcode() const { | 4507 intptr_t FieldHashcode() const { |
| 4621 return (kind_ == kField) ? reinterpret_cast<intptr_t>(field().raw()) | 4508 return (kind_ == kField) ? reinterpret_cast<intptr_t>(field().raw()) |
| 4622 : offset_in_bytes_; | 4509 : offset_in_bytes_; |
| 4623 } | 4510 } |
| 4624 | 4511 |
| (...skipping 1822 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6447 return false; | 6334 return false; |
| 6448 } | 6335 } |
| 6449 } | 6336 } |
| 6450 | 6337 |
| 6451 | 6338 |
| 6452 void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { | 6339 void ConstantPropagator::VisitEqualityCompare(EqualityCompareInstr* instr) { |
| 6453 const Object& left = instr->left()->definition()->constant_value(); | 6340 const Object& left = instr->left()->definition()->constant_value(); |
| 6454 const Object& right = instr->right()->definition()->constant_value(); | 6341 const Object& right = instr->right()->definition()->constant_value(); |
| 6455 | 6342 |
| 6456 if (instr->left()->definition() == instr->right()->definition()) { | 6343 if (instr->left()->definition() == instr->right()->definition()) { |
| 6457 // Fold x == x, and x != x to true/false for numbers and checked strict | 6344 // Fold x == x, and x != x to true/false for numbers comparisons. |
| 6458 // comparisons. | 6345 if (RawObject::IsIntegerClassId(instr->operation_cid())) { |
| 6459 if (instr->IsCheckedStrictEqual() || | |
| 6460 RawObject::IsIntegerClassId(instr->operation_cid())) { | |
| 6461 return SetValue(instr, Bool::Get(instr->kind() == Token::kEQ)); | 6346 return SetValue(instr, Bool::Get(instr->kind() == Token::kEQ)); |
| 6462 } | 6347 } |
| 6463 } | 6348 } |
| 6464 | 6349 |
| 6465 if (IsNonConstant(left) || IsNonConstant(right)) { | 6350 if (IsNonConstant(left) || IsNonConstant(right)) { |
| 6466 SetValue(instr, non_constant_); | 6351 SetValue(instr, non_constant_); |
| 6467 } else if (IsConstant(left) && IsConstant(right)) { | 6352 } else if (IsConstant(left) && IsConstant(right)) { |
| 6468 if (left.IsInteger() && right.IsInteger()) { | 6353 if (left.IsInteger() && right.IsInteger()) { |
| 6469 const bool result = CompareIntegers(instr->kind(), | 6354 const bool result = CompareIntegers(instr->kind(), |
| 6470 Integer::Cast(left), | 6355 Integer::Cast(left), |
| (...skipping 950 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7421 comparison->kind(), | 7306 comparison->kind(), |
| 7422 left, | 7307 left, |
| 7423 right); | 7308 right); |
| 7424 } else if (comparison->IsEqualityCompare()) { | 7309 } else if (comparison->IsEqualityCompare()) { |
| 7425 EqualityCompareInstr* equality_compare = comparison->AsEqualityCompare(); | 7310 EqualityCompareInstr* equality_compare = comparison->AsEqualityCompare(); |
| 7426 EqualityCompareInstr* new_equality_compare = | 7311 EqualityCompareInstr* new_equality_compare = |
| 7427 new EqualityCompareInstr(equality_compare->token_pos(), | 7312 new EqualityCompareInstr(equality_compare->token_pos(), |
| 7428 comparison->kind(), | 7313 comparison->kind(), |
| 7429 left, | 7314 left, |
| 7430 right, | 7315 right, |
| 7431 Object::null_array()); | 7316 equality_compare->operation_cid(), |
| 7432 new_equality_compare->set_ic_data(equality_compare->ic_data()); | 7317 equality_compare->deopt_id()); |
| 7433 new_equality_compare->set_operation_cid(equality_compare->operation_cid()); | |
| 7434 new_comparison = new_equality_compare; | 7318 new_comparison = new_equality_compare; |
| 7435 } else { | 7319 } else { |
| 7436 ASSERT(comparison->IsRelationalOp()); | 7320 ASSERT(comparison->IsRelationalOp()); |
| 7437 RelationalOpInstr* relational_op = comparison->AsRelationalOp(); | 7321 RelationalOpInstr* relational_op = comparison->AsRelationalOp(); |
| 7438 RelationalOpInstr* new_relational_op = | 7322 RelationalOpInstr* new_relational_op = |
| 7439 new RelationalOpInstr(relational_op->token_pos(), | 7323 new RelationalOpInstr(relational_op->token_pos(), |
| 7440 comparison->kind(), | 7324 comparison->kind(), |
| 7441 left, | 7325 left, |
| 7442 right, | 7326 right, |
| 7443 relational_op->operation_cid(), | 7327 relational_op->operation_cid(), |
| (...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7967 } | 7851 } |
| 7968 | 7852 |
| 7969 // Insert materializations at environment uses. | 7853 // Insert materializations at environment uses. |
| 7970 for (intptr_t i = 0; i < exits.length(); i++) { | 7854 for (intptr_t i = 0; i < exits.length(); i++) { |
| 7971 CreateMaterializationAt(exits[i], alloc, alloc->cls(), *fields); | 7855 CreateMaterializationAt(exits[i], alloc, alloc->cls(), *fields); |
| 7972 } | 7856 } |
| 7973 } | 7857 } |
| 7974 | 7858 |
| 7975 | 7859 |
| 7976 } // namespace dart | 7860 } // namespace dart |
| OLD | NEW |