| 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 #ifndef DART_PRECOMPILED_RUNTIME | 4 #ifndef DART_PRECOMPILED_RUNTIME |
| 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 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 if (!call->with_checks()) { | 202 if (!call->with_checks()) { |
| 203 return; // Already specialized. | 203 return; // Already specialized. |
| 204 } | 204 } |
| 205 | 205 |
| 206 const intptr_t receiver_cid = | 206 const intptr_t receiver_cid = |
| 207 call->PushArgumentAt(0)->value()->Type()->ToCid(); | 207 call->PushArgumentAt(0)->value()->Type()->ToCid(); |
| 208 if (receiver_cid == kDynamicCid) { | 208 if (receiver_cid == kDynamicCid) { |
| 209 return; // No information about receiver was infered. | 209 return; // No information about receiver was infered. |
| 210 } | 210 } |
| 211 | 211 |
| 212 const ICData& ic_data = *call->instance_call()->ic_data(); | 212 const ICData& ic_data = FlowGraphCompiler::TrySpecializeICDataByReceiverCid( |
| 213 | 213 call->ic_data(), receiver_cid); |
| 214 const CallTargets* targets = | 214 if (ic_data.raw() == call->ic_data().raw()) { |
| 215 FlowGraphCompiler::ResolveCallTargetsForReceiverCid( | |
| 216 receiver_cid, String::Handle(zone(), ic_data.target_name()), | |
| 217 Array::Handle(zone(), ic_data.arguments_descriptor())); | |
| 218 if (targets == NULL) { | |
| 219 // No specialization. | 215 // No specialization. |
| 220 return; | 216 return; |
| 221 } | 217 } |
| 222 | 218 |
| 223 const bool with_checks = false; | 219 const bool with_checks = false; |
| 224 const bool complete = false; | 220 const bool complete = false; |
| 225 PolymorphicInstanceCallInstr* specialized = | 221 PolymorphicInstanceCallInstr* specialized = |
| 226 new (Z) PolymorphicInstanceCallInstr(call->instance_call(), *targets, | 222 new (Z) PolymorphicInstanceCallInstr(call->instance_call(), ic_data, |
| 227 with_checks, complete); | 223 with_checks, complete); |
| 228 call->ReplaceWith(specialized, current_iterator()); | 224 call->ReplaceWith(specialized, current_iterator()); |
| 229 } | 225 } |
| 230 | 226 |
| 231 | 227 |
| 232 static bool ClassIdIsOneOf(intptr_t class_id, | 228 static bool ClassIdIsOneOf(intptr_t class_id, |
| 233 const GrowableArray<intptr_t>& class_ids) { | 229 const GrowableArray<intptr_t>& class_ids) { |
| 234 for (intptr_t i = 0; i < class_ids.length(); i++) { | 230 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 235 ASSERT(class_ids[i] != kIllegalCid); | 231 ASSERT(class_ids[i] != kIllegalCid); |
| 236 if (class_ids[i] == class_id) { | 232 if (class_ids[i] == class_id) { |
| (...skipping 1208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1445 } | 1441 } |
| 1446 } | 1442 } |
| 1447 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( | 1443 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( |
| 1448 call->token_pos(), new (Z) Value(left), | 1444 call->token_pos(), new (Z) Value(left), |
| 1449 new (Z) Value(instantiator_type_args), new (Z) Value(function_type_args), | 1445 new (Z) Value(instantiator_type_args), new (Z) Value(function_type_args), |
| 1450 type, Symbols::InTypeCast(), call->deopt_id()); | 1446 type, Symbols::InTypeCast(), call->deopt_id()); |
| 1451 ReplaceCall(call, assert_as); | 1447 ReplaceCall(call, assert_as); |
| 1452 } | 1448 } |
| 1453 | 1449 |
| 1454 | 1450 |
| 1451 bool JitOptimizer::LookupMethodFor(int class_id, |
| 1452 const ArgumentsDescriptor& args_desc, |
| 1453 const String& name, |
| 1454 Function* fn_return) { |
| 1455 if (class_id < 0) return false; |
| 1456 if (class_id >= I->class_table()->NumCids()) return false; |
| 1457 |
| 1458 RawClass* raw_class = I->class_table()->At(class_id); |
| 1459 if (raw_class == NULL) return false; |
| 1460 Class& cls = Class::Handle(Z, raw_class); |
| 1461 if (cls.IsNull()) return false; |
| 1462 if (!cls.is_finalized()) return false; |
| 1463 if (Array::Handle(cls.functions()).IsNull()) return false; |
| 1464 |
| 1465 bool allow_add = false; |
| 1466 Function& target_function = |
| 1467 Function::Handle(Z, Resolver::ResolveDynamicForReceiverClass( |
| 1468 cls, name, args_desc, allow_add)); |
| 1469 if (target_function.IsNull()) return false; |
| 1470 *fn_return ^= target_function.raw(); |
| 1471 return true; |
| 1472 } |
| 1473 |
| 1474 |
| 1475 static int OrderById(const intptr_t* a, const intptr_t* b) { |
| 1476 // Negative if 'a' should sort before 'b'. |
| 1477 return *a - *b; |
| 1478 } |
| 1479 |
| 1480 |
| 1481 void JitOptimizer::TryExpandClassesInICData(const ICData& ic_data) { |
| 1482 if (ic_data.NumberOfChecks() == 0) return; |
| 1483 |
| 1484 Function& dummy = Function::Handle(Z); |
| 1485 |
| 1486 GrowableArray<intptr_t> ids; |
| 1487 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { |
| 1488 // The API works for multi dispatch ICs that check more than one argument, |
| 1489 // but we know we only check one arg here, so only the 0th element of id |
| 1490 // will be used. |
| 1491 GrowableArray<intptr_t> id; |
| 1492 ic_data.GetCheckAt(i, &id, &dummy); |
| 1493 ids.Add(id[0]); |
| 1494 } |
| 1495 ids.Sort(OrderById); |
| 1496 |
| 1497 Array& args_desc_array = Array::Handle(Z, ic_data.arguments_descriptor()); |
| 1498 ArgumentsDescriptor args_desc(args_desc_array); |
| 1499 String& name = String::Handle(Z, ic_data.target_name()); |
| 1500 |
| 1501 Function& fn = Function::Handle(Z); |
| 1502 Function& fn_high = Function::Handle(Z); |
| 1503 Function& possible_match = Function::Handle(Z); |
| 1504 |
| 1505 for (int cid_index = 0; cid_index < ids.length() - 1; cid_index++) { |
| 1506 int low_cid = ids[cid_index]; |
| 1507 int high_cid = ids[cid_index + 1]; |
| 1508 if (low_cid + 1 == high_cid) continue; |
| 1509 if (LookupMethodFor(low_cid, args_desc, name, &fn) && |
| 1510 LookupMethodFor(high_cid, args_desc, name, &fn_high) && |
| 1511 fn.raw() == fn_high.raw()) { |
| 1512 // Try to fill in the IC table by going downwards from a known class-id. |
| 1513 bool can_fill_in = true; |
| 1514 for (int i = low_cid + 1; i < high_cid; i++) { |
| 1515 if (!LookupMethodFor(i, args_desc, name, &possible_match) || |
| 1516 possible_match.raw() != fn.raw()) { |
| 1517 can_fill_in = false; |
| 1518 break; |
| 1519 } |
| 1520 } |
| 1521 if (can_fill_in) { |
| 1522 for (int i = low_cid + 1; i < high_cid; i++) { |
| 1523 ic_data.AddReceiverCheck(i, fn, 0); |
| 1524 } |
| 1525 } |
| 1526 } |
| 1527 } |
| 1528 } |
| 1529 |
| 1455 // Tries to optimize instance call by replacing it with a faster instruction | 1530 // Tries to optimize instance call by replacing it with a faster instruction |
| 1456 // (e.g, binary op, field load, ..). | 1531 // (e.g, binary op, field load, ..). |
| 1457 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1532 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 1458 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | 1533 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { |
| 1459 return; | 1534 return; |
| 1460 } | 1535 } |
| 1461 const Token::Kind op_kind = instr->token_kind(); | 1536 const Token::Kind op_kind = instr->token_kind(); |
| 1462 | 1537 |
| 1463 // Type test is special as it always gets converted into inlined code. | 1538 // Type test is special as it always gets converted into inlined code. |
| 1464 if (Token::IsTypeTestOperator(op_kind)) { | 1539 if (Token::IsTypeTestOperator(op_kind)) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1502 return; | 1577 return; |
| 1503 } | 1578 } |
| 1504 if ((op_kind == Token::kSET) && | 1579 if ((op_kind == Token::kSET) && |
| 1505 TryInlineInstanceSetter(instr, unary_checks)) { | 1580 TryInlineInstanceSetter(instr, unary_checks)) { |
| 1506 return; | 1581 return; |
| 1507 } | 1582 } |
| 1508 if (TryInlineInstanceMethod(instr)) { | 1583 if (TryInlineInstanceMethod(instr)) { |
| 1509 return; | 1584 return; |
| 1510 } | 1585 } |
| 1511 | 1586 |
| 1512 CallTargets* targets = CallTargets::Create(Z, unary_checks); | 1587 // Now we are done trying the inlining options that benefit from only having |
| 1588 // 1 entry in the IC table. |
| 1589 TryExpandClassesInICData(unary_checks); |
| 1513 | 1590 |
| 1514 bool has_one_target = targets->HasSingleTarget(); | 1591 bool has_one_target = unary_checks.HasOneTarget(); |
| 1515 | 1592 |
| 1516 if (has_one_target) { | 1593 if (has_one_target) { |
| 1517 // Check if the single target is a polymorphic target, if it is, | 1594 // Check if the single target is a polymorphic target, if it is, |
| 1518 // we don't have one target. | 1595 // we don't have one target. |
| 1519 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1596 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1520 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { | 1597 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
| 1521 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( | 1598 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
| 1522 *targets) != Type::null(); | 1599 unary_checks) != Type::null(); |
| 1523 } else { | 1600 } else { |
| 1524 const bool polymorphic_target = | 1601 const bool polymorphic_target = |
| 1525 MethodRecognizer::PolymorphicTarget(target); | 1602 MethodRecognizer::PolymorphicTarget(target); |
| 1526 has_one_target = !polymorphic_target; | 1603 has_one_target = !polymorphic_target; |
| 1527 } | 1604 } |
| 1528 } | 1605 } |
| 1529 | 1606 |
| 1530 if (has_one_target) { | 1607 if (has_one_target) { |
| 1531 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1608 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1532 const RawFunction::Kind function_kind = target.kind(); | 1609 const RawFunction::Kind function_kind = target.kind(); |
| 1533 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1610 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 1534 PolymorphicInstanceCallInstr* call = | 1611 PolymorphicInstanceCallInstr* call = |
| 1535 new (Z) PolymorphicInstanceCallInstr(instr, *targets, | 1612 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 1536 /* call_with_checks = */ false, | 1613 /* call_with_checks = */ false, |
| 1537 /* complete = */ false); | 1614 /* complete = */ false); |
| 1538 instr->ReplaceWith(call, current_iterator()); | 1615 instr->ReplaceWith(call, current_iterator()); |
| 1539 return; | 1616 return; |
| 1540 } | 1617 } |
| 1541 } | 1618 } |
| 1542 | 1619 |
| 1543 // If there is only one target we can make this into a deopting class check, | 1620 // If there is only one target we can make this into a deopting class check, |
| 1544 // followed by a call instruction that does not check the class of the | 1621 // followed by a call instruction that does not check the class of the |
| 1545 // receiver. This enables a lot of optimizations because after the class | 1622 // receiver. This enables a lot of optimizations because after the class |
| 1546 // check we can probably inline the call and not worry about side effects. | 1623 // check we can probably inline the call and not worry about side effects. |
| 1547 // However, this can fall down if new receiver classes arrive at this call | 1624 // However, this can fall down if new receiver classes arrive at this call |
| 1548 // site after we generated optimized code. This causes a deopt, and after a | 1625 // site after we generated optimized code. This causes a deopt, and after a |
| 1549 // few deopts we won't optimize this function any more at all. Therefore for | 1626 // few deopts we won't optimize this function any more at all. Therefore for |
| 1550 // very polymorphic sites we don't make this optimization, keeping it as a | 1627 // very polymorphic sites we don't make this optimization, keeping it as a |
| 1551 // regular checked PolymorphicInstanceCall, which falls back to the slow but | 1628 // regular checked PolymorphicInstanceCall, which falls back to the slow but |
| 1552 // non-deopting megamorphic call stub when it sees new receiver classes. | 1629 // non-deopting megamorphic call stub when it sees new receiver classes. |
| 1553 bool call_with_checks; | 1630 bool call_with_checks; |
| 1554 if (has_one_target && FLAG_polymorphic_with_deopt && | 1631 if (has_one_target && FLAG_polymorphic_with_deopt && |
| 1555 (!instr->ic_data()->HasDeoptReason(ICData::kDeoptCheckClass) || | 1632 (!instr->ic_data()->HasDeoptReason(ICData::kDeoptCheckClass) || |
| 1556 unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { | 1633 unary_checks.NumberOfChecks() <= FLAG_max_polymorphic_checks)) { |
| 1557 // Type propagation has not run yet, we cannot eliminate the check. | 1634 // Type propagation has not run yet, we cannot eliminate the check. |
| 1558 // TODO(erikcorry): The receiver check should use the off-heap targets | |
| 1559 // array, not the IC array. | |
| 1560 AddReceiverCheck(instr); | 1635 AddReceiverCheck(instr); |
| 1561 // Call can still deoptimize, do not detach environment from instr. | 1636 // Call can still deoptimize, do not detach environment from instr. |
| 1562 call_with_checks = false; | 1637 call_with_checks = false; |
| 1563 } else { | 1638 } else { |
| 1564 call_with_checks = true; | 1639 call_with_checks = true; |
| 1565 } | 1640 } |
| 1566 PolymorphicInstanceCallInstr* call = | 1641 PolymorphicInstanceCallInstr* call = new (Z) |
| 1567 new (Z) PolymorphicInstanceCallInstr(instr, *targets, call_with_checks, | 1642 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, |
| 1568 /* complete = */ false); | 1643 /* complete = */ false); |
| 1569 instr->ReplaceWith(call, current_iterator()); | 1644 instr->ReplaceWith(call, current_iterator()); |
| 1570 } | 1645 } |
| 1571 | 1646 |
| 1572 | 1647 |
| 1573 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { | 1648 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { |
| 1574 MethodRecognizer::Kind recognized_kind = | 1649 MethodRecognizer::Kind recognized_kind = |
| 1575 MethodRecognizer::RecognizeKind(call->function()); | 1650 MethodRecognizer::RecognizeKind(call->function()); |
| 1576 switch (recognized_kind) { | 1651 switch (recognized_kind) { |
| 1577 case MethodRecognizer::kObjectConstructor: | 1652 case MethodRecognizer::kObjectConstructor: |
| 1578 case MethodRecognizer::kObjectArrayAllocate: | 1653 case MethodRecognizer::kObjectArrayAllocate: |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1813 // Discard the environment from the original instruction because the store | 1888 // Discard the environment from the original instruction because the store |
| 1814 // can't deoptimize. | 1889 // can't deoptimize. |
| 1815 instr->RemoveEnvironment(); | 1890 instr->RemoveEnvironment(); |
| 1816 ReplaceCall(instr, store); | 1891 ReplaceCall(instr, store); |
| 1817 return true; | 1892 return true; |
| 1818 } | 1893 } |
| 1819 | 1894 |
| 1820 | 1895 |
| 1821 } // namespace dart | 1896 } // namespace dart |
| 1822 #endif // DART_PRECOMPILED_RUNTIME | 1897 #endif // DART_PRECOMPILED_RUNTIME |
| OLD | NEW |