Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: runtime/vm/jit_optimizer.cc

Issue 2809583002: Use off-heap data for type feedback in PolymorphicInstanceCallInstr (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698