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 #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 |