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 = FlowGraphCompiler::TrySpecializeICDataByReceiverCid( | 212 const ICData& ic_data = *call->instance_call()->ic_data(); |
Vyacheslav Egorov (Google)
2017/04/10 10:59:28
disregarding ic_data has an interesting effect on
erikcorry
2017/04/19 15:06:40
I'm not sure where this is happening. If it's in
| |
213 call->ic_data(), receiver_cid); | 213 |
214 if (ic_data.raw() == call->ic_data().raw()) { | 214 const PolymorphicTargets* targets = |
215 FlowGraphCompiler::TrySpecializeByReceiverCid( | |
216 Array::Handle(zone(), ic_data.arguments_descriptor()), | |
217 String::Handle(zone(), ic_data.target_name()), receiver_cid); | |
218 if (targets == NULL) { | |
215 // No specialization. | 219 // No specialization. |
216 return; | 220 return; |
217 } | 221 } |
218 | 222 |
219 const bool with_checks = false; | 223 const bool with_checks = false; |
220 const bool complete = false; | 224 const bool complete = false; |
221 PolymorphicInstanceCallInstr* specialized = | 225 PolymorphicInstanceCallInstr* specialized = |
222 new (Z) PolymorphicInstanceCallInstr(call->instance_call(), ic_data, | 226 new (Z) PolymorphicInstanceCallInstr(call->instance_call(), *targets, |
223 with_checks, complete); | 227 with_checks, complete); |
224 call->ReplaceWith(specialized, current_iterator()); | 228 call->ReplaceWith(specialized, current_iterator()); |
225 } | 229 } |
226 | 230 |
227 | 231 |
228 static bool ClassIdIsOneOf(intptr_t class_id, | 232 static bool ClassIdIsOneOf(intptr_t class_id, |
229 const GrowableArray<intptr_t>& class_ids) { | 233 const GrowableArray<intptr_t>& class_ids) { |
230 for (intptr_t i = 0; i < class_ids.length(); i++) { | 234 for (intptr_t i = 0; i < class_ids.length(); i++) { |
231 ASSERT(class_ids[i] != kIllegalCid); | 235 ASSERT(class_ids[i] != kIllegalCid); |
232 if (class_ids[i] == class_id) { | 236 if (class_ids[i] == class_id) { |
(...skipping 1204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1437 } | 1441 } |
1438 } | 1442 } |
1439 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( | 1443 AssertAssignableInstr* assert_as = new (Z) AssertAssignableInstr( |
1440 call->token_pos(), new (Z) Value(left), new (Z) Value(type_args), | 1444 call->token_pos(), new (Z) Value(left), new (Z) Value(type_args), |
1441 NULL, // TODO(regis): Pass function type arguments. | 1445 NULL, // TODO(regis): Pass function type arguments. |
1442 type, Symbols::InTypeCast(), call->deopt_id()); | 1446 type, Symbols::InTypeCast(), call->deopt_id()); |
1443 ReplaceCall(call, assert_as); | 1447 ReplaceCall(call, assert_as); |
1444 } | 1448 } |
1445 | 1449 |
1446 | 1450 |
1447 bool JitOptimizer::LookupMethodFor(int class_id, | 1451 static int OrderById(const CidRangeTarget* a, const CidRangeTarget* b) { |
1448 const ArgumentsDescriptor& args_desc, | 1452 // Negative if 'a' should sort before 'b'. |
1449 const String& name, | 1453 ASSERT(a->cid_start == a->cid_end); |
1450 Function* fn_return) { | 1454 ASSERT(b->cid_start == b->cid_end); |
1451 if (class_id < 0) return false; | 1455 return a->cid_start - b->cid_start; |
1452 if (class_id >= I->class_table()->NumCids()) return false; | |
1453 | |
1454 RawClass* raw_class = I->class_table()->At(class_id); | |
1455 if (raw_class == NULL) return false; | |
1456 Class& cls = Class::Handle(Z, raw_class); | |
1457 if (cls.IsNull()) return false; | |
1458 if (!cls.is_finalized()) return false; | |
1459 if (Array::Handle(cls.functions()).IsNull()) return false; | |
1460 | |
1461 Function& target_function = Function::Handle( | |
1462 Z, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc)); | |
1463 if (target_function.IsNull()) return false; | |
1464 *fn_return ^= target_function.raw(); | |
1465 return true; | |
1466 } | 1456 } |
1467 | 1457 |
1468 | 1458 |
1469 static int OrderById(const intptr_t* a, const intptr_t* b) { | 1459 static int OrderByFrequency(const CidRangeTarget* a, const CidRangeTarget* b) { |
1470 // Negative if 'a' should sort before 'b'. | 1460 // Negative if 'a' should sort before 'b'. |
1471 return *a - *b; | 1461 return b->count - a->count; |
1472 } | 1462 } |
1473 | 1463 |
1474 | 1464 |
1475 void JitOptimizer::TryExpandClassesInICData(const ICData& ic_data) { | 1465 PolymorphicTargets* JitOptimizer::CreatePolymorphicTargets( |
Vyacheslav Egorov (Google)
2017/04/10 10:59:28
Given that this method is used by both Aot and Jit
erikcorry
2017/04/19 15:06:40
Moved to static CallTargets::Create(...)
| |
1476 if (ic_data.NumberOfChecks() == 0) return; | 1466 Zone* zone, |
1467 const ICData& ic_data) { | |
1468 PolymorphicTargets* targets = new (zone) PolymorphicTargets(zone); | |
1469 if (ic_data.NumberOfChecks() == 0) return targets; | |
1477 | 1470 |
1478 Function& dummy = Function::Handle(Z); | 1471 Function& dummy = Function::Handle(zone); |
1479 | 1472 |
1480 GrowableArray<intptr_t> ids; | 1473 bool check_one_arg = ic_data.NumArgsTested() == 1; |
1481 for (int i = 0; i < ic_data.NumberOfChecks(); i++) { | 1474 |
1482 // The API works for multi dispatch ICs that check more than one argument, | 1475 int checks = ic_data.NumberOfChecks(); |
1483 // but we know we only check one arg here, so only the 0th element of id | 1476 for (int i = 0; i < checks; i++) { |
1484 // will be used. | 1477 intptr_t id = 0; |
1485 GrowableArray<intptr_t> id; | 1478 if (check_one_arg) { |
1486 ic_data.GetCheckAt(i, &id, &dummy); | 1479 ic_data.GetOneClassCheckAt(i, &id, &dummy); |
1487 ids.Add(id[0]); | 1480 } else { |
1481 // The API works for multi dispatch ICs that check more than one | |
1482 // argument, but we know we will only check one arg here, so only the 0th | |
1483 // element of id will be used. | |
1484 GrowableArray<intptr_t> arg_ids; | |
1485 ic_data.GetCheckAt(i, &arg_ids, &dummy); | |
1486 id = arg_ids[0]; | |
1487 } | |
1488 Function& function = Function::ZoneHandle(zone, ic_data.GetTargetAt(i)); | |
1489 targets->Add(CidRangeTarget(id, id, &function, ic_data.GetCountAt(i))); | |
1488 } | 1490 } |
1489 ids.Sort(OrderById); | |
1490 | 1491 |
1491 Array& args_desc_array = Array::Handle(Z, ic_data.arguments_descriptor()); | 1492 targets->Sort(OrderById); |
1493 | |
1494 Array& args_desc_array = Array::Handle(zone, ic_data.arguments_descriptor()); | |
1492 ArgumentsDescriptor args_desc(args_desc_array); | 1495 ArgumentsDescriptor args_desc(args_desc_array); |
1493 String& name = String::Handle(Z, ic_data.target_name()); | 1496 String& name = String::Handle(zone, ic_data.target_name()); |
1494 | 1497 |
1495 Function& fn = Function::Handle(Z); | 1498 Function& fn = Function::Handle(zone); |
1496 Function& fn_high = Function::Handle(Z); | |
1497 Function& possible_match = Function::Handle(Z); | |
1498 | 1499 |
1499 for (int cid_index = 0; cid_index < ids.length() - 1; cid_index++) { | 1500 intptr_t length = targets->length(); |
1500 int low_cid = ids[cid_index]; | 1501 |
1501 int high_cid = ids[cid_index + 1]; | 1502 // Spread class-ids to preceding classes where a lookup yields the same |
1502 if (low_cid + 1 == high_cid) continue; | 1503 // method. |
1503 if (LookupMethodFor(low_cid, args_desc, name, &fn) && | 1504 for (int idx = 0; idx < length; idx++) { |
1504 LookupMethodFor(high_cid, args_desc, name, &fn_high) && | 1505 int lower_limit_cid = (idx == 0) ? -1 : targets->At(idx - 1).cid_end; |
1505 fn.raw() == fn_high.raw()) { | 1506 const Function& target = *targets->At(idx).target; |
1506 // Try to fill in the IC table by going downwards from a known class-id. | 1507 for (int i = targets->At(idx).cid_start - 1; i > lower_limit_cid; i--) { |
1507 bool can_fill_in = true; | 1508 if (FlowGraphCompiler::LookupMethodFor(i, args_desc, name, &fn) && |
1508 for (int i = low_cid + 1; i < high_cid; i++) { | 1509 fn.raw() == target.raw()) { |
1509 if (!LookupMethodFor(i, args_desc, name, &possible_match) || | 1510 CidRangeTarget t = targets->At(idx); |
1510 possible_match.raw() != fn.raw()) { | 1511 t.cid_start = i; |
1511 can_fill_in = false; | 1512 (*targets)[idx] = t; |
1512 break; | 1513 } else { |
1513 } | 1514 break; |
1514 } | |
1515 if (can_fill_in) { | |
1516 for (int i = low_cid + 1; i < high_cid; i++) { | |
1517 ic_data.AddReceiverCheck(i, fn, 0); | |
1518 } | |
1519 } | 1515 } |
1520 } | 1516 } |
1521 } | 1517 } |
1518 // Spread class-ids to following classes where a lookup yields the same | |
1519 // method. | |
1520 for (int idx = 0; idx < length; idx++) { | |
1521 int upper_limit_cid = | |
1522 (idx == length - 1) ? 1000000000 : targets->At(idx + 1).cid_start; | |
1523 const Function& target = *targets->At(idx).target; | |
1524 for (int i = targets->At(idx).cid_end + 1; i < upper_limit_cid; i++) { | |
1525 if (FlowGraphCompiler::LookupMethodFor(i, args_desc, name, &fn) && | |
1526 fn.raw() == target.raw()) { | |
1527 (*targets)[idx].cid_end = i; | |
1528 } else { | |
1529 break; | |
1530 } | |
1531 } | |
1532 } | |
1533 // Merge adjacent class id ranges. | |
1534 int dest = 0; | |
1535 for (int src = 1; src < length; src++) { | |
1536 if (targets->At(dest).cid_end + 1 == targets->At(src).cid_start && | |
1537 targets->At(dest).target->raw() == targets->At(src).target->raw()) { | |
1538 (*targets)[dest].cid_end = targets->At(src).cid_end; | |
1539 (*targets)[dest].count += targets->At(src).count; | |
1540 } else { | |
1541 dest++; | |
1542 if (src != dest) (*targets)[dest] = targets->At(src); | |
1543 } | |
1544 } | |
1545 targets->SetLength(dest + 1); | |
1546 targets->Sort(OrderByFrequency); | |
1547 return targets; | |
1522 } | 1548 } |
1523 | 1549 |
1524 // Tries to optimize instance call by replacing it with a faster instruction | 1550 // Tries to optimize instance call by replacing it with a faster instruction |
1525 // (e.g, binary op, field load, ..). | 1551 // (e.g, binary op, field load, ..). |
1526 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1552 void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
1527 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { | 1553 if (!instr->HasICData() || (instr->ic_data()->NumberOfUsedChecks() == 0)) { |
1528 return; | 1554 return; |
1529 } | 1555 } |
1530 const Token::Kind op_kind = instr->token_kind(); | 1556 const Token::Kind op_kind = instr->token_kind(); |
1531 | 1557 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1571 return; | 1597 return; |
1572 } | 1598 } |
1573 if ((op_kind == Token::kSET) && | 1599 if ((op_kind == Token::kSET) && |
1574 TryInlineInstanceSetter(instr, unary_checks)) { | 1600 TryInlineInstanceSetter(instr, unary_checks)) { |
1575 return; | 1601 return; |
1576 } | 1602 } |
1577 if (TryInlineInstanceMethod(instr)) { | 1603 if (TryInlineInstanceMethod(instr)) { |
1578 return; | 1604 return; |
1579 } | 1605 } |
1580 | 1606 |
1581 // Now we are done trying the inlining options that benefit from only having | 1607 PolymorphicTargets* targets = CreatePolymorphicTargets(Z, unary_checks); |
1582 // 1 entry in the IC table. | |
1583 TryExpandClassesInICData(unary_checks); | |
1584 | 1608 |
1585 bool has_one_target = unary_checks.HasOneTarget(); | 1609 bool has_one_target = targets->HasSingleTarget(); |
1586 | 1610 |
1587 if (has_one_target) { | 1611 if (has_one_target) { |
1588 // Check if the single target is a polymorphic target, if it is, | 1612 // Check if the single target is a polymorphic target, if it is, |
1589 // we don't have one target. | 1613 // we don't have one target. |
1590 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1614 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
1591 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { | 1615 if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
1592 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( | 1616 has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
1593 unary_checks) != Type::null(); | 1617 *targets) != Type::null(); |
1594 } else { | 1618 } else { |
1595 const bool polymorphic_target = | 1619 const bool polymorphic_target = |
1596 MethodRecognizer::PolymorphicTarget(target); | 1620 MethodRecognizer::PolymorphicTarget(target); |
1597 has_one_target = !polymorphic_target; | 1621 has_one_target = !polymorphic_target; |
1598 } | 1622 } |
1599 } | 1623 } |
1600 | 1624 |
1601 if (has_one_target) { | 1625 if (has_one_target) { |
1602 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1626 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
1603 const RawFunction::Kind function_kind = target.kind(); | 1627 const RawFunction::Kind function_kind = target.kind(); |
1604 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1628 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
1605 PolymorphicInstanceCallInstr* call = | 1629 PolymorphicInstanceCallInstr* call = |
1606 new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, | 1630 new (Z) PolymorphicInstanceCallInstr(instr, *targets, |
1607 /* call_with_checks = */ false, | 1631 /* call_with_checks = */ false, |
1608 /* complete = */ false); | 1632 /* complete = */ false); |
1609 instr->ReplaceWith(call, current_iterator()); | 1633 instr->ReplaceWith(call, current_iterator()); |
1610 return; | 1634 return; |
1611 } | 1635 } |
1612 } | 1636 } |
1613 | 1637 |
1614 bool call_with_checks; | 1638 bool call_with_checks; |
1615 if (has_one_target && FLAG_polymorphic_with_deopt) { | 1639 if (has_one_target && FLAG_polymorphic_with_deopt) { |
1616 // Type propagation has not run yet, we cannot eliminate the check. | 1640 // Type propagation has not run yet, we cannot eliminate the check. |
1641 // TODO(erikcorry): The receiver check should use the off-heap targets | |
1642 // array, not the IC array. | |
1617 AddReceiverCheck(instr); | 1643 AddReceiverCheck(instr); |
1618 // Call can still deoptimize, do not detach environment from instr. | 1644 // Call can still deoptimize, do not detach environment from instr. |
1619 call_with_checks = false; | 1645 call_with_checks = false; |
1620 } else { | 1646 } else { |
1621 call_with_checks = true; | 1647 call_with_checks = true; |
1622 } | 1648 } |
1623 PolymorphicInstanceCallInstr* call = new (Z) | 1649 PolymorphicInstanceCallInstr* call = |
1624 PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, | 1650 new (Z) PolymorphicInstanceCallInstr(instr, *targets, call_with_checks, |
1625 /* complete = */ false); | 1651 /* complete = */ false); |
1626 instr->ReplaceWith(call, current_iterator()); | 1652 instr->ReplaceWith(call, current_iterator()); |
1627 } | 1653 } |
1628 | 1654 |
1629 | 1655 |
1630 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { | 1656 void JitOptimizer::VisitStaticCall(StaticCallInstr* call) { |
1631 MethodRecognizer::Kind recognized_kind = | 1657 MethodRecognizer::Kind recognized_kind = |
1632 MethodRecognizer::RecognizeKind(call->function()); | 1658 MethodRecognizer::RecognizeKind(call->function()); |
1633 switch (recognized_kind) { | 1659 switch (recognized_kind) { |
1634 case MethodRecognizer::kObjectConstructor: | 1660 case MethodRecognizer::kObjectConstructor: |
1635 case MethodRecognizer::kObjectArrayAllocate: | 1661 case MethodRecognizer::kObjectArrayAllocate: |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1870 // Discard the environment from the original instruction because the store | 1896 // Discard the environment from the original instruction because the store |
1871 // can't deoptimize. | 1897 // can't deoptimize. |
1872 instr->RemoveEnvironment(); | 1898 instr->RemoveEnvironment(); |
1873 ReplaceCall(instr, store); | 1899 ReplaceCall(instr, store); |
1874 return true; | 1900 return true; |
1875 } | 1901 } |
1876 | 1902 |
1877 | 1903 |
1878 } // namespace dart | 1904 } // namespace dart |
1879 #endif // DART_PRECOMPILED_RUNTIME | 1905 #endif // DART_PRECOMPILED_RUNTIME |
OLD | NEW |