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 1458 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1469 return; | 1469 return; |
1470 } | 1470 } |
1471 } | 1471 } |
1472 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( | 1472 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( |
1473 call->token_pos(), new (Z) Value(left), new (Z) Value(type_args), type, | 1473 call->token_pos(), new (Z) Value(left), new (Z) Value(type_args), type, |
1474 Symbols::InTypeCast(), call->deopt_id()); | 1474 Symbols::InTypeCast(), call->deopt_id()); |
1475 ReplaceCall(call, assert_as); | 1475 ReplaceCall(call, assert_as); |
1476 } | 1476 } |
1477 | 1477 |
1478 | 1478 |
1479 bool JitOptimizer::LookupMethodFor(int class_id, | |
1480 const ArgumentsDescriptor& args_desc, | |
1481 const String& name, | |
1482 Function* fn_return) { | |
1483 if (class_id < 0) return false; | |
1484 if (class_id >= I->class_table()->NumCids()) return false; | |
1485 | |
1486 RawClass* raw_class = I->class_table()->At(class_id); | |
1487 if (raw_class == NULL) return false; | |
1488 Class& cls = Class::Handle(Z, raw_class); | |
1489 if (cls.IsNull()) return false; | |
1490 if (!cls.is_finalized()) return false; | |
1491 if (Array::Handle(cls.functions()).IsNull()) return false; | |
1492 | |
1493 Function& target_function = Function::Handle( | |
1494 Z, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc)); | |
rmacnak
2017/04/06 22:24:52
This lookup introduced a race with the mutator whe
| |
1495 if (target_function.IsNull()) return false; | |
1496 *fn_return ^= target_function.raw(); | |
1497 return true; | |
1498 } | |
1499 | |
1500 | |
1501 static int OrderById(const intptr_t* a, const intptr_t* b) { | |
1502 // Negative if 'a' should sort before 'b'. | |
1503 return *a - *b; | |
1504 } | |
1505 | |
1506 | |
1507 void JitOptimizer::TryExpandClassesInICData(const ICData& ic_data) { | |
1508 if (ic_data.NumberOfChecks() == 0) return; | |
1509 | |
1510 Function& dummy = Function::Handle(Z); | |
1511 | |
1512 GrowableArray<intptr_t> ids; | |
1513 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { | |
1514 // The API works for multi dispatch ICs that check more than one argument, | |
1515 // but we know we only check one arg here, so only the 0th element of id | |
1516 // will be used. | |
1517 GrowableArray<intptr_t> id; | |
1518 ic_data.GetCheckAt(i, &id, &dummy); | |
1519 ids.Add(id[0]); | |
1520 } | |
1521 ids.Sort(OrderById); | |
1522 | |
1523 Array& args_desc_array = Array::Handle(Z, ic_data.arguments_descriptor()); | |
1524 ArgumentsDescriptor args_desc(args_desc_array); | |
1525 String& name = String::Handle(Z, ic_data.target_name()); | |
1526 | |
1527 Function& fn = Function::Handle(Z); | |
1528 Function& fn_high = Function::Handle(Z); | |
1529 Function& possible_match = Function::Handle(Z); | |
1530 | |
1531 for (int cid_index = 0; cid_index < ids.length() - 1; cid_index++) { | |
1532 int low_cid = ids[cid_index]; | |
1533 int high_cid = ids[cid_index + 1]; | |
1534 if (low_cid + 1 == high_cid) continue; | |
1535 if (LookupMethodFor(low_cid, args_desc, name, &fn) && | |
1536 LookupMethodFor(high_cid, args_desc, name, &fn_high) && | |
1537 fn.raw() == fn_high.raw()) { | |
1538 // Try to fill in the IC table by going downwards from a known class-id. | |
1539 bool can_fill_in = true; | |
1540 for (int i = low_cid + 1; i < high_cid; i++) { | |
1541 if (!LookupMethodFor(i, args_desc, name, &possible_match) || | |
1542 possible_match.raw() != fn.raw()) { | |
1543 can_fill_in = false; | |
1544 break; | |
1545 } | |
1546 } | |
1547 if (can_fill_in) { | |
1548 for (int i = low_cid + 1; i < high_cid; i++) { | |
1549 ic_data.AddReceiverCheck(i, fn, 0); | |
1550 } | |
1551 } | |
1552 } | |
1553 } | |
1554 } | |
1555 | |
1479 // Tries to optimize instance call by replacing it with a faster instruction | 1556 // Tries to optimize instance call by replacing it with a faster instruction |
1480 // (e.g, binary op, field load, ..). | 1557 // (e.g, binary op, field load, ..). |
1481 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1558 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
1482 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | 1559 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { |
1483 return; | 1560 return; |
1484 } | 1561 } |
1485 const Token::Kind op_kind = instr->token_kind(); | 1562 const Token::Kind op_kind = instr->token_kind(); |
1486 | 1563 |
1487 // Type test is special as it always gets converted into inlined code. | 1564 // Type test is special as it always gets converted into inlined code. |
1488 if (Token::IsTypeTestOperator(op_kind)) { | 1565 if (Token::IsTypeTestOperator(op_kind)) { |
1489 ReplaceWithInstanceOf(instr); | 1566 ReplaceWithInstanceOf(instr); |
1490 return; | 1567 return; |
1491 } | 1568 } |
1492 | 1569 |
1493 if (Token::IsTypeCastOperator(op_kind)) { | 1570 if (Token::IsTypeCastOperator(op_kind)) { |
1494 ReplaceWithTypeCast(instr); | 1571 ReplaceWithTypeCast(instr); |
1495 return; | 1572 return; |
1496 } | 1573 } |
1497 | 1574 |
1498 const ICData& unary_checks = | 1575 const ICData& unary_checks = |
1499 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | 1576 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
1500 | 1577 |
1501 const bool is_dense = CheckClassInstr::IsDenseCidRange(unary_checks); | |
1502 const intptr_t max_checks = (op_kind == Token::kEQ) | |
1503 ? FLAG_max_equality_polymorphic_checks | |
1504 : FLAG_max_polymorphic_checks; | |
1505 const intptr_t number_of_checks = unary_checks.NumberOfChecks(); | |
1506 if ((number_of_checks > max_checks) && !is_dense && | |
1507 flow_graph()->InstanceCallNeedsClassCheck( | |
1508 instr, RawFunction::kRegularFunction)) { | |
1509 // Too many checks, it will be megamorphic which needs unary checks. | |
1510 instr->set_ic_data(&unary_checks); | |
1511 return; | |
1512 } | |
1513 | |
1514 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { | 1578 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { |
1515 return; | 1579 return; |
1516 } | 1580 } |
1517 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { | 1581 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { |
1518 return; | 1582 return; |
1519 } | 1583 } |
1520 | 1584 |
1521 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { | 1585 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { |
1522 return; | 1586 return; |
1523 } | 1587 } |
(...skipping 15 matching lines...) Expand all Loading... | |
1539 return; | 1603 return; |
1540 } | 1604 } |
1541 if ((op_kind == Token::kSET) && | 1605 if ((op_kind == Token::kSET) && |
1542 TryInlineInstanceSetter(instr, unary_checks)) { | 1606 TryInlineInstanceSetter(instr, unary_checks)) { |
1543 return; | 1607 return; |
1544 } | 1608 } |
1545 if (TryInlineInstanceMethod(instr)) { | 1609 if (TryInlineInstanceMethod(instr)) { |
1546 return; | 1610 return; |
1547 } | 1611 } |
1548 | 1612 |
1613 // Now we are done trying the inlining options that benefit from only having | |
1614 // 1 entry in the IC table. | |
1615 TryExpandClassesInICData(unary_checks); | |
1616 | |
1549 bool has_one_target = unary_checks.HasOneTarget(); | 1617 bool has_one_target = unary_checks.HasOneTarget(); |
1550 | 1618 |
1551 if (has_one_target) { | 1619 if (has_one_target) { |
1552 // Check if the single target is a polymorphic target, if it is, | 1620 // Check if the single target is a polymorphic target, if it is, |
1553 // we don't have one target. | 1621 // we don't have one target. |
1554 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1622 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
1555 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { | 1623 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
1556 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( | 1624 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
1557 unary_checks) != Type::null(); | 1625 unary_checks) != Type::null(); |
1558 } else { | 1626 } else { |
1559 const bool polymorphic_target = | 1627 const bool polymorphic_target = |
1560 MethodRecognizer::PolymorphicTarget(target); | 1628 MethodRecognizer::PolymorphicTarget(target); |
1561 has_one_target = !polymorphic_target; | 1629 has_one_target = !polymorphic_target; |
1562 } | 1630 } |
1563 } | 1631 } |
1564 | 1632 |
1565 if (has_one_target) { | 1633 if (has_one_target) { |
1566 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1634 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
1567 const RawFunction::Kind function_kind = target.kind(); | 1635 const RawFunction::Kind function_kind = target.kind(); |
1568 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1636 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
1569 PolymorphicInstanceCallInstr* call = | 1637 PolymorphicInstanceCallInstr* call = |
1570 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 1638 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
1571 /* call_with_checks = */ false, | 1639 /* call_with_checks = */ false, |
1572 /* complete = */ false); | 1640 /* complete = */ false); |
1573 instr->ReplaceWith(call, current_iterator()); | 1641 instr->ReplaceWith(call, current_iterator()); |
1574 return; | 1642 return; |
1575 } | 1643 } |
1576 } | 1644 } |
1577 | 1645 |
1578 if (number_of_checks <= FLAG_max_polymorphic_checks || | 1646 bool call_with_checks; |
1579 (has_one_target && is_dense)) { | 1647 if (has_one_target && FLAG_polymorphic_with_deopt) { |
1580 bool call_with_checks; | 1648 // Type propagation has not run yet, we cannot eliminate the check. |
1581 if (has_one_target && FLAG_polymorphic_with_deopt) { | 1649 AddReceiverCheck(instr); |
1582 // Type propagation has not run yet, we cannot eliminate the check. | 1650 // Call can still deoptimize, do not detach environment from instr. |
1583 AddReceiverCheck(instr); | 1651 call_with_checks = false; |
1584 // Call can still deoptimize, do not detach environment from instr. | 1652 } else { |
1585 call_with_checks = false; | 1653 call_with_checks = true; |
1586 } else { | |
1587 call_with_checks = true; | |
1588 } | |
1589 PolymorphicInstanceCallInstr* call = new (Z) | |
1590 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, | |
1591 /* complete = */ false); | |
1592 instr->ReplaceWith(call, current_iterator()); | |
1593 } | 1654 } |
1655 PolymorphicInstanceCallInstr* call = new (Z) | |
1656 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, | |
1657 /* complete = */ false); | |
1658 instr->ReplaceWith(call, current_iterator()); | |
1594 } | 1659 } |
1595 | 1660 |
1596 | 1661 |
1597 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { | 1662 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { |
1598 MethodRecognizer::Kind recognized_kind = | 1663 MethodRecognizer::Kind recognized_kind = |
1599 MethodRecognizer::RecognizeKind(call->function()); | 1664 MethodRecognizer::RecognizeKind(call->function()); |
1600 switch (recognized_kind) { | 1665 switch (recognized_kind) { |
1601 case MethodRecognizer::kObjectConstructor: | 1666 case MethodRecognizer::kObjectConstructor: |
1602 case MethodRecognizer::kObjectArrayAllocate: | 1667 case MethodRecognizer::kObjectArrayAllocate: |
1603 case MethodRecognizer::kFloat32x4Zero: | 1668 case MethodRecognizer::kFloat32x4Zero: |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1837 // Discard the environment from the original instruction because the store | 1902 // Discard the environment from the original instruction because the store |
1838 // can't deoptimize. | 1903 // can't deoptimize. |
1839 instr->RemoveEnvironment(); | 1904 instr->RemoveEnvironment(); |
1840 ReplaceCall(instr, store); | 1905 ReplaceCall(instr, store); |
1841 return true; | 1906 return true; |
1842 } | 1907 } |
1843 | 1908 |
1844 | 1909 |
1845 } // namespace dart | 1910 } // namespace dart |
1846 #endif // DART_PRECOMPILED_RUNTIME | 1911 #endif // DART_PRECOMPILED_RUNTIME |
OLD | NEW |