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 Class& clarse = Class::Handle(Z, I->class_table()->At(class_id)); | |
|
Vyacheslav Egorov (Google)
2017/03/10 10:31:30
please use cls
erikcorry
2017/03/10 13:30:01
Done.
| |
| 1484 if (clarse.IsNull()) return false; | |
| 1485 if (!clarse.is_finalized()) return false; | |
| 1486 Function& target_function = Function::Handle( | |
| 1487 Z, Resolver::ResolveDynamicForReceiverClass(clarse, name, args_desc)); | |
| 1488 if (target_function.IsNull()) return false; | |
| 1489 *fn_return ^= target_function.raw(); | |
| 1490 return true; | |
| 1491 } | |
| 1492 | |
| 1493 | |
| 1494 bool JitOptimizer::TryAddClass(const ICData& ic_data, | |
|
Vyacheslav Egorov (Google)
2017/03/10 10:31:31
This function is called TryAddClass but it does no
erikcorry
2017/03/10 13:30:01
This method was removed.
| |
| 1495 const Function& match, | |
| 1496 int class_id, | |
| 1497 const ArgumentsDescriptor& args_desc, | |
| 1498 const String& name) { | |
| 1499 if (class_id < 0) return false; | |
| 1500 if (class_id >= I->class_table()->NumCids()) return false; | |
| 1501 | |
| 1502 RawClass* raw_class = I->class_table()->At(class_id); | |
|
Vyacheslav Egorov (Google)
2017/03/10 10:31:30
This seems to almost duplicate the code in the Loo
erikcorry
2017/03/10 13:30:01
Done.
| |
| 1503 if (raw_class == NULL) return false; | |
| 1504 Class& try_class = Class::Handle(Z, raw_class); | |
| 1505 | |
| 1506 if (!try_class.is_finalized()) return false; | |
| 1507 if (Array::Handle(try_class.functions()).IsNull()) return false; | |
| 1508 Function& target_function = Function::Handle( | |
| 1509 Z, Resolver::ResolveDynamicForReceiverClass(try_class, name, args_desc)); | |
| 1510 if (target_function.IsNull()) return false; | |
| 1511 if (target_function.raw() != match.raw()) return false; | |
| 1512 return true; | |
| 1513 } | |
| 1514 | |
| 1515 static int OrderById(const intptr_t* a, const intptr_t* b) { | |
| 1516 // Negative if 'a' should sort before 'b'. | |
| 1517 return *a - *b; | |
| 1518 } | |
| 1519 | |
| 1520 void JitOptimizer::TryExpandClassesInIC(const ICData& ic_data) { | |
| 1521 if (ic_data.NumberOfChecks() == 0) return; | |
| 1522 | |
| 1523 Function& dummy = Function::Handle(Z); | |
| 1524 | |
| 1525 GrowableArray<intptr_t> ids; | |
| 1526 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { | |
| 1527 // The API works for multi dispatch ICs that check more than one argument, | |
| 1528 // but we know we only check one arg here, so only the 0th element of id | |
| 1529 // will be used. | |
| 1530 GrowableArray<intptr_t> id; | |
| 1531 ic_data.GetCheckAt(i, &id, &dummy); | |
| 1532 ids.Add(id[0]); | |
| 1533 } | |
| 1534 ids.Sort(OrderById); | |
| 1535 | |
| 1536 Array& args_desc_array = Array::Handle(Z, ic_data.arguments_descriptor()); | |
| 1537 ArgumentsDescriptor args_desc(args_desc_array); | |
| 1538 String& name = String::Handle(Z, ic_data.target_name()); | |
| 1539 | |
| 1540 Function& fn = Function::Handle(Z); | |
| 1541 Function& fn_high = Function::Handle(Z); | |
| 1542 | |
| 1543 for (int cid_index = 0; cid_index < ids.length() - 1; cid_index++) { | |
| 1544 int low_cid = ids[cid_index]; | |
| 1545 int high_cid = ids[cid_index + 1]; | |
| 1546 if (low_cid + 1 == high_cid) continue; | |
| 1547 if (LookupMethodFor(low_cid, args_desc, name, &fn) && | |
| 1548 LookupMethodFor(high_cid, args_desc, name, &fn_high) && | |
| 1549 fn.raw() == fn_high.raw()) { | |
| 1550 // Try to fill in the IC table by going downwards from a known class-id. | |
| 1551 bool can_fill_in = true; | |
| 1552 for (int i = low_cid + 1; i < high_cid; i++) { | |
| 1553 if (!TryAddClass(ic_data, fn, i, args_desc, name)) { | |
| 1554 can_fill_in = false; | |
| 1555 break; | |
| 1556 } | |
| 1557 } | |
| 1558 if (can_fill_in) { | |
| 1559 for (int i = low_cid + 1; i < high_cid; i++) { | |
| 1560 ic_data.AddReceiverCheck(i, fn, 0); | |
| 1561 } | |
| 1562 } | |
| 1563 } | |
| 1564 } | |
| 1565 } | |
| 1566 | |
| 1479 // Tries to optimize instance call by replacing it with a faster instruction | 1567 // Tries to optimize instance call by replacing it with a faster instruction |
| 1480 // (e.g, binary op, field load, ..). | 1568 // (e.g, binary op, field load, ..). |
| 1481 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1569 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 1482 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | 1570 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { |
| 1483 return; | 1571 return; |
| 1484 } | 1572 } |
| 1485 const Token::Kind op_kind = instr->token_kind(); | 1573 const Token::Kind op_kind = instr->token_kind(); |
| 1486 | 1574 |
| 1487 // Type test is special as it always gets converted into inlined code. | 1575 // Type test is special as it always gets converted into inlined code. |
| 1488 if (Token::IsTypeTestOperator(op_kind)) { | 1576 if (Token::IsTypeTestOperator(op_kind)) { |
| 1489 ReplaceWithInstanceOf(instr); | 1577 ReplaceWithInstanceOf(instr); |
| 1490 return; | 1578 return; |
| 1491 } | 1579 } |
| 1492 | 1580 |
| 1493 if (Token::IsTypeCastOperator(op_kind)) { | 1581 if (Token::IsTypeCastOperator(op_kind)) { |
| 1494 ReplaceWithTypeCast(instr); | 1582 ReplaceWithTypeCast(instr); |
| 1495 return; | 1583 return; |
| 1496 } | 1584 } |
| 1497 | 1585 |
| 1498 const ICData& unary_checks = | 1586 const ICData& unary_checks = |
| 1499 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | 1587 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
| 1500 | 1588 |
| 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 | |
|
Vyacheslav Egorov (Google)
2017/03/10 10:31:30
If you killed the code, kill the flag FLAG_max_equ
erikcorry
2017/03/10 13:30:01
Killed the flag.
Original code review has no rati
erikcorry
2017/03/10 13:36:54
I got confused here between the two flags. I'll ch
| |
| 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)) { | 1589 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { |
| 1515 return; | 1590 return; |
| 1516 } | 1591 } |
| 1517 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { | 1592 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { |
| 1518 return; | 1593 return; |
| 1519 } | 1594 } |
| 1520 | 1595 |
| 1521 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { | 1596 if (op_kind == Token::kEQ && TryReplaceWithEqualityOp(instr, op_kind)) { |
| 1522 return; | 1597 return; |
| 1523 } | 1598 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 1539 return; | 1614 return; |
| 1540 } | 1615 } |
| 1541 if ((op_kind == Token::kSET) && | 1616 if ((op_kind == Token::kSET) && |
| 1542 TryInlineInstanceSetter(instr, unary_checks)) { | 1617 TryInlineInstanceSetter(instr, unary_checks)) { |
| 1543 return; | 1618 return; |
| 1544 } | 1619 } |
| 1545 if (TryInlineInstanceMethod(instr)) { | 1620 if (TryInlineInstanceMethod(instr)) { |
| 1546 return; | 1621 return; |
| 1547 } | 1622 } |
| 1548 | 1623 |
| 1624 // Now we are done trying the inlining options that benefit from only having | |
| 1625 // 1 entry in the IC table. | |
| 1626 TryExpandClassesInIC(unary_checks); | |
| 1627 | |
| 1549 bool has_one_target = unary_checks.HasOneTarget(); | 1628 bool has_one_target = unary_checks.HasOneTarget(); |
| 1550 | 1629 |
| 1551 if (has_one_target) { | 1630 if (has_one_target) { |
| 1552 // Check if the single target is a polymorphic target, if it is, | 1631 // Check if the single target is a polymorphic target, if it is, |
| 1553 // we don't have one target. | 1632 // we don't have one target. |
| 1554 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1633 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1555 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { | 1634 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
| 1556 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( | 1635 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
| 1557 unary_checks) != Type::null(); | 1636 unary_checks) != Type::null(); |
| 1558 } else { | 1637 } else { |
| 1559 const bool polymorphic_target = | 1638 const bool polymorphic_target = |
| 1560 MethodRecognizer::PolymorphicTarget(target); | 1639 MethodRecognizer::PolymorphicTarget(target); |
| 1561 has_one_target = !polymorphic_target; | 1640 has_one_target = !polymorphic_target; |
| 1562 } | 1641 } |
| 1563 } | 1642 } |
| 1564 | 1643 |
| 1565 if (has_one_target) { | 1644 if (has_one_target) { |
| 1566 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1645 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| 1567 const RawFunction::Kind function_kind = target.kind(); | 1646 const RawFunction::Kind function_kind = target.kind(); |
| 1568 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1647 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
| 1569 PolymorphicInstanceCallInstr* call = | 1648 PolymorphicInstanceCallInstr* call = |
| 1570 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 1649 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| 1571 /* call_with_checks = */ false, | 1650 /* call_with_checks = */ false, |
| 1572 /* complete = */ false); | 1651 /* complete = */ false); |
| 1573 instr->ReplaceWith(call, current_iterator()); | 1652 instr->ReplaceWith(call, current_iterator()); |
| 1574 return; | 1653 return; |
| 1575 } | 1654 } |
| 1576 } | 1655 } |
| 1577 | 1656 |
| 1578 if (number_of_checks <= FLAG_max_polymorphic_checks || | 1657 bool call_with_checks; |
| 1579 (has_one_target && is_dense)) { | 1658 if (has_one_target && FLAG_polymorphic_with_deopt) { |
| 1580 bool call_with_checks; | 1659 // Type propagation has not run yet, we cannot eliminate the check. |
| 1581 if (has_one_target && FLAG_polymorphic_with_deopt) { | 1660 AddReceiverCheck(instr); |
| 1582 // Type propagation has not run yet, we cannot eliminate the check. | 1661 // Call can still deoptimize, do not detach environment from instr. |
| 1583 AddReceiverCheck(instr); | 1662 call_with_checks = false; |
| 1584 // Call can still deoptimize, do not detach environment from instr. | 1663 } else { |
| 1585 call_with_checks = false; | 1664 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 } | 1665 } |
| 1666 PolymorphicInstanceCallInstr* call = new (Z) | |
| 1667 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, | |
| 1668 /* complete = */ false); | |
| 1669 instr->ReplaceWith(call, current_iterator()); | |
| 1594 } | 1670 } |
| 1595 | 1671 |
| 1596 | 1672 |
| 1597 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { | 1673 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { |
| 1598 MethodRecognizer::Kind recognized_kind = | 1674 MethodRecognizer::Kind recognized_kind = |
| 1599 MethodRecognizer::RecognizeKind(call->function()); | 1675 MethodRecognizer::RecognizeKind(call->function()); |
| 1600 switch (recognized_kind) { | 1676 switch (recognized_kind) { |
| 1601 case MethodRecognizer::kObjectConstructor: | 1677 case MethodRecognizer::kObjectConstructor: |
| 1602 case MethodRecognizer::kObjectArrayAllocate: | 1678 case MethodRecognizer::kObjectArrayAllocate: |
| 1603 case MethodRecognizer::kFloat32x4Zero: | 1679 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 | 1913 // Discard the environment from the original instruction because the store |
| 1838 // can't deoptimize. | 1914 // can't deoptimize. |
| 1839 instr->RemoveEnvironment(); | 1915 instr->RemoveEnvironment(); |
| 1840 ReplaceCall(instr, store); | 1916 ReplaceCall(instr, store); |
| 1841 return true; | 1917 return true; |
| 1842 } | 1918 } |
| 1843 | 1919 |
| 1844 | 1920 |
| 1845 } // namespace dart | 1921 } // namespace dart |
| 1846 #endif // DART_PRECOMPILED_RUNTIME | 1922 #endif // DART_PRECOMPILED_RUNTIME |
| OLD | NEW |