| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 | 4 |
| 5 #include "vm/flow_graph.h" | 5 #include "vm/flow_graph.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/flow_graph_builder.h" | 8 #include "vm/flow_graph_builder.h" |
| 9 #include "vm/flow_graph_compiler.h" |
| 10 #include "vm/flow_graph_range_analysis.h" |
| 9 #include "vm/il_printer.h" | 11 #include "vm/il_printer.h" |
| 10 #include "vm/intermediate_language.h" | 12 #include "vm/intermediate_language.h" |
| 11 #include "vm/growable_array.h" | 13 #include "vm/growable_array.h" |
| 12 #include "vm/object_store.h" | 14 #include "vm/object_store.h" |
| 13 #include "vm/report.h" | 15 #include "vm/report.h" |
| 14 | 16 |
| 15 namespace dart { | 17 namespace dart { |
| 16 | 18 |
| 19 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32) |
| 20 DEFINE_FLAG(bool, trace_smi_widening, false, "Trace Smi->Int32 widening pass."); |
| 21 #endif |
| 17 DEFINE_FLAG(bool, prune_dead_locals, true, "optimize dead locals away"); | 22 DEFINE_FLAG(bool, prune_dead_locals, true, "optimize dead locals away"); |
| 18 DECLARE_FLAG(bool, emit_edge_counters); | 23 DECLARE_FLAG(bool, emit_edge_counters); |
| 19 DECLARE_FLAG(bool, reorder_basic_blocks); | 24 DECLARE_FLAG(bool, reorder_basic_blocks); |
| 20 DECLARE_FLAG(bool, trace_optimization); | 25 DECLARE_FLAG(bool, trace_optimization); |
| 21 DECLARE_FLAG(bool, verify_compiler); | 26 DECLARE_FLAG(bool, verify_compiler); |
| 22 | 27 |
| 23 | 28 |
| 24 FlowGraph::FlowGraph(const ParsedFunction& parsed_function, | 29 FlowGraph::FlowGraph(const ParsedFunction& parsed_function, |
| 25 GraphEntryInstr* graph_entry, | 30 GraphEntryInstr* graph_entry, |
| 26 intptr_t max_block_id) | 31 intptr_t max_block_id) |
| (...skipping 1399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1426 IsSideEffectFreePath(block, instr->GetBlock()); | 1431 IsSideEffectFreePath(block, instr->GetBlock()); |
| 1427 } | 1432 } |
| 1428 | 1433 |
| 1429 | 1434 |
| 1430 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, | 1435 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, |
| 1431 BlockEntryInstr* to) const { | 1436 BlockEntryInstr* to) const { |
| 1432 return available_at_[to->postorder_number()]->Contains( | 1437 return available_at_[to->postorder_number()]->Contains( |
| 1433 from->postorder_number()); | 1438 from->postorder_number()); |
| 1434 } | 1439 } |
| 1435 | 1440 |
| 1441 |
| 1442 // Quick access to the current zone. |
| 1443 #define Z (zone()) |
| 1444 |
| 1445 |
| 1446 void FlowGraph::ConvertUse(Value* use, Representation from_rep) { |
| 1447 const Representation to_rep = |
| 1448 use->instruction()->RequiredInputRepresentation(use->use_index()); |
| 1449 if (from_rep == to_rep || to_rep == kNoRepresentation) { |
| 1450 return; |
| 1451 } |
| 1452 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ false); |
| 1453 } |
| 1454 |
| 1455 |
| 1456 static bool IsUnboxedInteger(Representation rep) { |
| 1457 return (rep == kUnboxedInt32) || |
| 1458 (rep == kUnboxedUint32) || |
| 1459 (rep == kUnboxedMint); |
| 1460 } |
| 1461 |
| 1462 |
| 1463 static bool ShouldInlineSimd() { |
| 1464 return FlowGraphCompiler::SupportsUnboxedSimd128(); |
| 1465 } |
| 1466 |
| 1467 |
| 1468 static bool CanUnboxDouble() { |
| 1469 return FlowGraphCompiler::SupportsUnboxedDoubles(); |
| 1470 } |
| 1471 |
| 1472 |
| 1473 static bool CanConvertUnboxedMintToDouble() { |
| 1474 return FlowGraphCompiler::CanConvertUnboxedMintToDouble(); |
| 1475 } |
| 1476 |
| 1477 |
| 1478 void FlowGraph::InsertConversion(Representation from, |
| 1479 Representation to, |
| 1480 Value* use, |
| 1481 bool is_environment_use) { |
| 1482 Instruction* insert_before; |
| 1483 Instruction* deopt_target; |
| 1484 PhiInstr* phi = use->instruction()->AsPhi(); |
| 1485 if (phi != NULL) { |
| 1486 ASSERT(phi->is_alive()); |
| 1487 // For phis conversions have to be inserted in the predecessor. |
| 1488 insert_before = |
| 1489 phi->block()->PredecessorAt(use->use_index())->last_instruction(); |
| 1490 deopt_target = NULL; |
| 1491 } else { |
| 1492 deopt_target = insert_before = use->instruction(); |
| 1493 } |
| 1494 |
| 1495 Definition* converted = NULL; |
| 1496 if (IsUnboxedInteger(from) && IsUnboxedInteger(to)) { |
| 1497 const intptr_t deopt_id = (to == kUnboxedInt32) && (deopt_target != NULL) ? |
| 1498 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; |
| 1499 converted = new(Z) UnboxedIntConverterInstr(from, |
| 1500 to, |
| 1501 use->CopyWithType(), |
| 1502 deopt_id); |
| 1503 } else if ((from == kUnboxedInt32) && (to == kUnboxedDouble)) { |
| 1504 converted = new Int32ToDoubleInstr(use->CopyWithType()); |
| 1505 } else if ((from == kUnboxedMint) && |
| 1506 (to == kUnboxedDouble) && |
| 1507 CanConvertUnboxedMintToDouble()) { |
| 1508 const intptr_t deopt_id = (deopt_target != NULL) ? |
| 1509 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; |
| 1510 ASSERT(CanUnboxDouble()); |
| 1511 converted = new MintToDoubleInstr(use->CopyWithType(), deopt_id); |
| 1512 } else if ((from == kTagged) && Boxing::Supports(to)) { |
| 1513 const intptr_t deopt_id = (deopt_target != NULL) ? |
| 1514 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; |
| 1515 converted = UnboxInstr::Create(to, use->CopyWithType(), deopt_id); |
| 1516 } else if ((to == kTagged) && Boxing::Supports(from)) { |
| 1517 converted = BoxInstr::Create(from, use->CopyWithType()); |
| 1518 } else { |
| 1519 // We have failed to find a suitable conversion instruction. |
| 1520 // Insert two "dummy" conversion instructions with the correct |
| 1521 // "from" and "to" representation. The inserted instructions will |
| 1522 // trigger a deoptimization if executed. See #12417 for a discussion. |
| 1523 const intptr_t deopt_id = (deopt_target != NULL) ? |
| 1524 deopt_target->DeoptimizationTarget() : Thread::kNoDeoptId; |
| 1525 ASSERT(Boxing::Supports(from)); |
| 1526 ASSERT(Boxing::Supports(to)); |
| 1527 Definition* boxed = BoxInstr::Create(from, use->CopyWithType()); |
| 1528 use->BindTo(boxed); |
| 1529 InsertBefore(insert_before, boxed, NULL, FlowGraph::kValue); |
| 1530 converted = UnboxInstr::Create(to, new(Z) Value(boxed), deopt_id); |
| 1531 } |
| 1532 ASSERT(converted != NULL); |
| 1533 InsertBefore(insert_before, converted, use->instruction()->env(), |
| 1534 FlowGraph::kValue); |
| 1535 if (is_environment_use) { |
| 1536 use->BindToEnvironment(converted); |
| 1537 } else { |
| 1538 use->BindTo(converted); |
| 1539 } |
| 1540 |
| 1541 if ((to == kUnboxedInt32) && (phi != NULL)) { |
| 1542 // Int32 phis are unboxed optimistically. Ensure that unboxing |
| 1543 // has deoptimization target attached from the goto instruction. |
| 1544 CopyDeoptTarget(converted, insert_before); |
| 1545 } |
| 1546 } |
| 1547 |
| 1548 |
| 1549 void FlowGraph::ConvertEnvironmentUse(Value* use, Representation from_rep) { |
| 1550 const Representation to_rep = kTagged; |
| 1551 if (from_rep == to_rep) { |
| 1552 return; |
| 1553 } |
| 1554 InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/ true); |
| 1555 } |
| 1556 |
| 1557 |
| 1558 void FlowGraph::InsertConversionsFor(Definition* def) { |
| 1559 const Representation from_rep = def->representation(); |
| 1560 |
| 1561 for (Value::Iterator it(def->input_use_list()); |
| 1562 !it.Done(); |
| 1563 it.Advance()) { |
| 1564 ConvertUse(it.Current(), from_rep); |
| 1565 } |
| 1566 |
| 1567 if (graph_entry()->SuccessorCount() > 1) { |
| 1568 for (Value::Iterator it(def->env_use_list()); |
| 1569 !it.Done(); |
| 1570 it.Advance()) { |
| 1571 Value* use = it.Current(); |
| 1572 if (use->instruction()->MayThrow() && |
| 1573 use->instruction()->GetBlock()->InsideTryBlock()) { |
| 1574 // Environment uses at calls inside try-blocks must be converted to |
| 1575 // tagged representation. |
| 1576 ConvertEnvironmentUse(it.Current(), from_rep); |
| 1577 } |
| 1578 } |
| 1579 } |
| 1580 } |
| 1581 |
| 1582 |
| 1583 static void UnboxPhi(PhiInstr* phi) { |
| 1584 Representation unboxed = phi->representation(); |
| 1585 |
| 1586 switch (phi->Type()->ToCid()) { |
| 1587 case kDoubleCid: |
| 1588 if (CanUnboxDouble()) { |
| 1589 unboxed = kUnboxedDouble; |
| 1590 } |
| 1591 break; |
| 1592 case kFloat32x4Cid: |
| 1593 if (ShouldInlineSimd()) { |
| 1594 unboxed = kUnboxedFloat32x4; |
| 1595 } |
| 1596 break; |
| 1597 case kInt32x4Cid: |
| 1598 if (ShouldInlineSimd()) { |
| 1599 unboxed = kUnboxedInt32x4; |
| 1600 } |
| 1601 break; |
| 1602 case kFloat64x2Cid: |
| 1603 if (ShouldInlineSimd()) { |
| 1604 unboxed = kUnboxedFloat64x2; |
| 1605 } |
| 1606 break; |
| 1607 } |
| 1608 |
| 1609 if ((kSmiBits < 32) && |
| 1610 (unboxed == kTagged) && |
| 1611 phi->Type()->IsInt() && |
| 1612 RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt64)) { |
| 1613 // On 32-bit platforms conservatively unbox phis that: |
| 1614 // - are proven to be of type Int; |
| 1615 // - fit into 64bits range; |
| 1616 // - have either constants or Box() operations as inputs; |
| 1617 // - have at least one Box() operation as an input; |
| 1618 // - are used in at least 1 Unbox() operation. |
| 1619 bool should_unbox = false; |
| 1620 for (intptr_t i = 0; i < phi->InputCount(); i++) { |
| 1621 Definition* input = phi->InputAt(i)->definition(); |
| 1622 if (input->IsBox() && |
| 1623 RangeUtils::Fits(input->range(), |
| 1624 RangeBoundary::kRangeBoundaryInt64)) { |
| 1625 should_unbox = true; |
| 1626 } else if (!input->IsConstant()) { |
| 1627 should_unbox = false; |
| 1628 break; |
| 1629 } |
| 1630 } |
| 1631 |
| 1632 if (should_unbox) { |
| 1633 // We checked inputs. Check if phi is used in at least one unbox |
| 1634 // operation. |
| 1635 bool has_unboxed_use = false; |
| 1636 for (Value* use = phi->input_use_list(); |
| 1637 use != NULL; |
| 1638 use = use->next_use()) { |
| 1639 Instruction* instr = use->instruction(); |
| 1640 if (instr->IsUnbox()) { |
| 1641 has_unboxed_use = true; |
| 1642 break; |
| 1643 } else if (IsUnboxedInteger( |
| 1644 instr->RequiredInputRepresentation(use->use_index()))) { |
| 1645 has_unboxed_use = true; |
| 1646 break; |
| 1647 } |
| 1648 } |
| 1649 |
| 1650 if (!has_unboxed_use) { |
| 1651 should_unbox = false; |
| 1652 } |
| 1653 } |
| 1654 |
| 1655 if (should_unbox) { |
| 1656 unboxed = |
| 1657 RangeUtils::Fits(phi->range(), RangeBoundary::kRangeBoundaryInt32) |
| 1658 ? kUnboxedInt32 : kUnboxedMint; |
| 1659 } |
| 1660 } |
| 1661 |
| 1662 phi->set_representation(unboxed); |
| 1663 } |
| 1664 |
| 1665 |
| 1666 void FlowGraph::SelectRepresentations() { |
| 1667 // Conservatively unbox all phis that were proven to be of Double, |
| 1668 // Float32x4, or Int32x4 type. |
| 1669 for (BlockIterator block_it = reverse_postorder_iterator(); |
| 1670 !block_it.Done(); |
| 1671 block_it.Advance()) { |
| 1672 JoinEntryInstr* join_entry = block_it.Current()->AsJoinEntry(); |
| 1673 if (join_entry != NULL) { |
| 1674 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) { |
| 1675 PhiInstr* phi = it.Current(); |
| 1676 UnboxPhi(phi); |
| 1677 } |
| 1678 } |
| 1679 } |
| 1680 |
| 1681 // Process all instructions and insert conversions where needed. |
| 1682 // Visit incoming parameters and constants. |
| 1683 for (intptr_t i = 0; |
| 1684 i < graph_entry()->initial_definitions()->length(); |
| 1685 i++) { |
| 1686 InsertConversionsFor((*graph_entry()->initial_definitions())[i]); |
| 1687 } |
| 1688 |
| 1689 for (BlockIterator block_it = reverse_postorder_iterator(); |
| 1690 !block_it.Done(); |
| 1691 block_it.Advance()) { |
| 1692 BlockEntryInstr* entry = block_it.Current(); |
| 1693 JoinEntryInstr* join_entry = entry->AsJoinEntry(); |
| 1694 if (join_entry != NULL) { |
| 1695 for (PhiIterator it(join_entry); !it.Done(); it.Advance()) { |
| 1696 PhiInstr* phi = it.Current(); |
| 1697 ASSERT(phi != NULL); |
| 1698 ASSERT(phi->is_alive()); |
| 1699 InsertConversionsFor(phi); |
| 1700 } |
| 1701 } |
| 1702 CatchBlockEntryInstr* catch_entry = entry->AsCatchBlockEntry(); |
| 1703 if (catch_entry != NULL) { |
| 1704 for (intptr_t i = 0; |
| 1705 i < catch_entry->initial_definitions()->length(); |
| 1706 i++) { |
| 1707 InsertConversionsFor((*catch_entry->initial_definitions())[i]); |
| 1708 } |
| 1709 } |
| 1710 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) { |
| 1711 Definition* def = it.Current()->AsDefinition(); |
| 1712 if (def != NULL) { |
| 1713 InsertConversionsFor(def); |
| 1714 } |
| 1715 } |
| 1716 } |
| 1717 } |
| 1718 |
| 1719 |
| 1720 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_IA32) |
| 1721 // Smi widening pass is only meaningful on platforms where Smi |
| 1722 // is smaller than 32bit. For now only support it on ARM and ia32. |
| 1723 static bool CanBeWidened(BinarySmiOpInstr* smi_op) { |
| 1724 return BinaryInt32OpInstr::IsSupported(smi_op->op_kind(), |
| 1725 smi_op->left(), |
| 1726 smi_op->right()); |
| 1727 } |
| 1728 |
| 1729 |
| 1730 static bool BenefitsFromWidening(BinarySmiOpInstr* smi_op) { |
| 1731 // TODO(vegorov): when shifts with non-constants shift count are supported |
| 1732 // add them here as we save untagging for the count. |
| 1733 switch (smi_op->op_kind()) { |
| 1734 case Token::kMUL: |
| 1735 case Token::kSHR: |
| 1736 // For kMUL we save untagging of the argument for kSHR |
| 1737 // we save tagging of the result. |
| 1738 return true; |
| 1739 |
| 1740 default: |
| 1741 return false; |
| 1742 } |
| 1743 } |
| 1744 |
| 1745 |
| 1746 void FlowGraph::WidenSmiToInt32() { |
| 1747 GrowableArray<BinarySmiOpInstr*> candidates; |
| 1748 |
| 1749 // Step 1. Collect all instructions that potentially benefit from widening of |
| 1750 // their operands (or their result) into int32 range. |
| 1751 for (BlockIterator block_it = reverse_postorder_iterator(); |
| 1752 !block_it.Done(); |
| 1753 block_it.Advance()) { |
| 1754 for (ForwardInstructionIterator instr_it(block_it.Current()); |
| 1755 !instr_it.Done(); |
| 1756 instr_it.Advance()) { |
| 1757 BinarySmiOpInstr* smi_op = instr_it.Current()->AsBinarySmiOp(); |
| 1758 if ((smi_op != NULL) && |
| 1759 smi_op->HasSSATemp() && |
| 1760 BenefitsFromWidening(smi_op) && |
| 1761 CanBeWidened(smi_op)) { |
| 1762 candidates.Add(smi_op); |
| 1763 } |
| 1764 } |
| 1765 } |
| 1766 |
| 1767 if (candidates.is_empty()) { |
| 1768 return; |
| 1769 } |
| 1770 |
| 1771 // Step 2. For each block in the graph compute which loop it belongs to. |
| 1772 // We will use this information later during computation of the widening's |
| 1773 // gain: we are going to assume that only conversion occuring inside the |
| 1774 // same loop should be counted against the gain, all other conversions |
| 1775 // can be hoisted and thus cost nothing compared to the loop cost itself. |
| 1776 const ZoneGrowableArray<BlockEntryInstr*>& loop_headers = LoopHeaders(); |
| 1777 |
| 1778 GrowableArray<intptr_t> loops(preorder().length()); |
| 1779 for (intptr_t i = 0; i < preorder().length(); i++) { |
| 1780 loops.Add(-1); |
| 1781 } |
| 1782 |
| 1783 for (intptr_t loop_id = 0; loop_id < loop_headers.length(); ++loop_id) { |
| 1784 for (BitVector::Iterator loop_it(loop_headers[loop_id]->loop_info()); |
| 1785 !loop_it.Done(); |
| 1786 loop_it.Advance()) { |
| 1787 loops[loop_it.Current()] = loop_id; |
| 1788 } |
| 1789 } |
| 1790 |
| 1791 // Step 3. For each candidate transitively collect all other BinarySmiOpInstr |
| 1792 // and PhiInstr that depend on it and that it depends on and count amount of |
| 1793 // untagging operations that we save in assumption that this whole graph of |
| 1794 // values is using kUnboxedInt32 representation instead of kTagged. |
| 1795 // Convert those graphs that have positive gain to kUnboxedInt32. |
| 1796 |
| 1797 // BitVector containing SSA indexes of all processed definitions. Used to skip |
| 1798 // those candidates that belong to dependency graph of another candidate. |
| 1799 BitVector* processed = |
| 1800 new(Z) BitVector(Z, current_ssa_temp_index()); |
| 1801 |
| 1802 // Worklist used to collect dependency graph. |
| 1803 DefinitionWorklist worklist(this, candidates.length()); |
| 1804 for (intptr_t i = 0; i < candidates.length(); i++) { |
| 1805 BinarySmiOpInstr* op = candidates[i]; |
| 1806 if (op->WasEliminated() || processed->Contains(op->ssa_temp_index())) { |
| 1807 continue; |
| 1808 } |
| 1809 |
| 1810 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1811 THR_Print("analysing candidate: %s\n", op->ToCString()); |
| 1812 } |
| 1813 worklist.Clear(); |
| 1814 worklist.Add(op); |
| 1815 |
| 1816 // Collect dependency graph. Note: more items are added to worklist |
| 1817 // inside this loop. |
| 1818 intptr_t gain = 0; |
| 1819 for (intptr_t j = 0; j < worklist.definitions().length(); j++) { |
| 1820 Definition* defn = worklist.definitions()[j]; |
| 1821 |
| 1822 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1823 THR_Print("> %s\n", defn->ToCString()); |
| 1824 } |
| 1825 |
| 1826 if (defn->IsBinarySmiOp() && |
| 1827 BenefitsFromWidening(defn->AsBinarySmiOp())) { |
| 1828 gain++; |
| 1829 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1830 THR_Print("^ [%" Pd "] (o) %s\n", gain, defn->ToCString()); |
| 1831 } |
| 1832 } |
| 1833 |
| 1834 const intptr_t defn_loop = loops[defn->GetBlock()->preorder_number()]; |
| 1835 |
| 1836 // Process all inputs. |
| 1837 for (intptr_t k = 0; k < defn->InputCount(); k++) { |
| 1838 Definition* input = defn->InputAt(k)->definition(); |
| 1839 if (input->IsBinarySmiOp() && |
| 1840 CanBeWidened(input->AsBinarySmiOp())) { |
| 1841 worklist.Add(input); |
| 1842 } else if (input->IsPhi() && (input->Type()->ToCid() == kSmiCid)) { |
| 1843 worklist.Add(input); |
| 1844 } else if (input->IsBinaryMintOp()) { |
| 1845 // Mint operation produces untagged result. We avoid tagging. |
| 1846 gain++; |
| 1847 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1848 THR_Print("^ [%" Pd "] (i) %s\n", gain, input->ToCString()); |
| 1849 } |
| 1850 } else if (defn_loop == loops[input->GetBlock()->preorder_number()] && |
| 1851 (input->Type()->ToCid() == kSmiCid)) { |
| 1852 // Input comes from the same loop, is known to be smi and requires |
| 1853 // untagging. |
| 1854 // TODO(vegorov) this heuristic assumes that values that are not |
| 1855 // known to be smi have to be checked and this check can be |
| 1856 // coalesced with untagging. Start coalescing them. |
| 1857 gain--; |
| 1858 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1859 THR_Print("v [%" Pd "] (i) %s\n", gain, input->ToCString()); |
| 1860 } |
| 1861 } |
| 1862 } |
| 1863 |
| 1864 // Process all uses. |
| 1865 for (Value* use = defn->input_use_list(); |
| 1866 use != NULL; |
| 1867 use = use->next_use()) { |
| 1868 Instruction* instr = use->instruction(); |
| 1869 Definition* use_defn = instr->AsDefinition(); |
| 1870 if (use_defn == NULL) { |
| 1871 // We assume that tagging before returning or pushing argument costs |
| 1872 // very little compared to the cost of the return/call itself. |
| 1873 if (!instr->IsReturn() && !instr->IsPushArgument()) { |
| 1874 gain--; |
| 1875 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1876 THR_Print("v [%" Pd "] (u) %s\n", |
| 1877 gain, |
| 1878 use->instruction()->ToCString()); |
| 1879 } |
| 1880 } |
| 1881 continue; |
| 1882 } else if (use_defn->IsBinarySmiOp() && |
| 1883 CanBeWidened(use_defn->AsBinarySmiOp())) { |
| 1884 worklist.Add(use_defn); |
| 1885 } else if (use_defn->IsPhi() && |
| 1886 use_defn->AsPhi()->Type()->ToCid() == kSmiCid) { |
| 1887 worklist.Add(use_defn); |
| 1888 } else if (use_defn->IsBinaryMintOp()) { |
| 1889 // BinaryMintOp requires untagging of its inputs. |
| 1890 // Converting kUnboxedInt32 to kUnboxedMint is essentially zero cost |
| 1891 // sign extension operation. |
| 1892 gain++; |
| 1893 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1894 THR_Print("^ [%" Pd "] (u) %s\n", |
| 1895 gain, |
| 1896 use->instruction()->ToCString()); |
| 1897 } |
| 1898 } else if (defn_loop == loops[instr->GetBlock()->preorder_number()]) { |
| 1899 gain--; |
| 1900 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1901 THR_Print("v [%" Pd "] (u) %s\n", |
| 1902 gain, |
| 1903 use->instruction()->ToCString()); |
| 1904 } |
| 1905 } |
| 1906 } |
| 1907 } |
| 1908 |
| 1909 processed->AddAll(worklist.contains_vector()); |
| 1910 |
| 1911 if (FLAG_support_il_printer && FLAG_trace_smi_widening) { |
| 1912 THR_Print("~ %s gain %" Pd "\n", op->ToCString(), gain); |
| 1913 } |
| 1914 |
| 1915 if (gain > 0) { |
| 1916 // We have positive gain from widening. Convert all BinarySmiOpInstr into |
| 1917 // BinaryInt32OpInstr and set representation of all phis to kUnboxedInt32. |
| 1918 for (intptr_t j = 0; j < worklist.definitions().length(); j++) { |
| 1919 Definition* defn = worklist.definitions()[j]; |
| 1920 ASSERT(defn->IsPhi() || defn->IsBinarySmiOp()); |
| 1921 |
| 1922 if (defn->IsBinarySmiOp()) { |
| 1923 BinarySmiOpInstr* smi_op = defn->AsBinarySmiOp(); |
| 1924 BinaryInt32OpInstr* int32_op = new(Z) BinaryInt32OpInstr( |
| 1925 smi_op->op_kind(), |
| 1926 smi_op->left()->CopyWithType(), |
| 1927 smi_op->right()->CopyWithType(), |
| 1928 smi_op->DeoptimizationTarget()); |
| 1929 |
| 1930 smi_op->ReplaceWith(int32_op, NULL); |
| 1931 } else if (defn->IsPhi()) { |
| 1932 defn->AsPhi()->set_representation(kUnboxedInt32); |
| 1933 ASSERT(defn->Type()->IsInt()); |
| 1934 } |
| 1935 } |
| 1936 } |
| 1937 } |
| 1938 } |
| 1939 #else |
| 1940 void FlowGraph::WidenSmiToInt32() { |
| 1941 // TODO(vegorov) ideally on 64-bit platforms we would like to narrow smi |
| 1942 // operations to 32-bit where it saves tagging and untagging and allows |
| 1943 // to use shorted (and faster) instructions. But we currently don't |
| 1944 // save enough range information in the ICData to drive this decision. |
| 1945 } |
| 1946 #endif |
| 1947 |
| 1948 |
| 1949 void FlowGraph::EliminateEnvironments() { |
| 1950 // After this pass we can no longer perform LICM and hoist instructions |
| 1951 // that can deoptimize. |
| 1952 |
| 1953 disallow_licm(); |
| 1954 for (BlockIterator block_it = reverse_postorder_iterator(); |
| 1955 !block_it.Done(); |
| 1956 block_it.Advance()) { |
| 1957 BlockEntryInstr* block = block_it.Current(); |
| 1958 block->RemoveEnvironment(); |
| 1959 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 1960 Instruction* current = it.Current(); |
| 1961 if (!current->CanDeoptimize()) { |
| 1962 // TODO(srdjan): --source-lines needs deopt environments to get at |
| 1963 // the code for this instruction, however, leaving the environment |
| 1964 // changes code. |
| 1965 current->RemoveEnvironment(); |
| 1966 } |
| 1967 } |
| 1968 } |
| 1969 } |
| 1970 |
| 1971 |
| 1972 bool FlowGraph::Canonicalize() { |
| 1973 bool changed = false; |
| 1974 |
| 1975 for (BlockIterator block_it = reverse_postorder_iterator(); |
| 1976 !block_it.Done(); |
| 1977 block_it.Advance()) { |
| 1978 for (ForwardInstructionIterator it(block_it.Current()); |
| 1979 !it.Done(); |
| 1980 it.Advance()) { |
| 1981 Instruction* current = it.Current(); |
| 1982 if (current->HasUnmatchedInputRepresentations()) { |
| 1983 // Can't canonicalize this instruction until all conversions for its |
| 1984 // inputs are inserted. |
| 1985 continue; |
| 1986 } |
| 1987 |
| 1988 Instruction* replacement = current->Canonicalize(this); |
| 1989 |
| 1990 if (replacement != current) { |
| 1991 // For non-definitions Canonicalize should return either NULL or |
| 1992 // this. |
| 1993 ASSERT((replacement == NULL) || current->IsDefinition()); |
| 1994 ReplaceCurrentInstruction(&it, current, replacement); |
| 1995 changed = true; |
| 1996 } |
| 1997 } |
| 1998 } |
| 1999 return changed; |
| 2000 } |
| 2001 |
| 2002 |
| 1436 } // namespace dart | 2003 } // namespace dart |
| OLD | NEW |