|
19 | 19 | #include "ir/iteration.h" |
20 | 20 | #include "ir/local-structural-dominance.h" |
21 | 21 | #include "ir/module-utils.h" |
| 22 | +#include "ir/subtype-exprs.h" |
22 | 23 | #include "ir/subtypes.h" |
23 | 24 | #include "ir/type-updating.h" |
24 | 25 | #include "support/string.h" |
@@ -1733,153 +1734,219 @@ void TranslateToFuzzReader::mutate(Function* func) { |
1733 | 1734 | // reasonable chance of making some changes. |
1734 | 1735 | percentChance = std::max(percentChance, Index(3)); |
1735 | 1736 |
|
| 1737 | + // First, find things to replace and their types. SubtypingDiscoverer needs to |
| 1738 | + // do this in a single, full walk (as types of children depend on parents, and |
| 1739 | + // even block targets). |
| 1740 | + struct Finder |
| 1741 | + : public ControlFlowWalker<Finder, SubtypingDiscoverer<Finder>> { |
| 1742 | + // Maps children we can replace to the types we can replace them with. We |
| 1743 | + // only store nontrivial ones (i.e., where the type is not just the child's |
| 1744 | + // type). |
| 1745 | + std::unordered_map<Expression*, Type> childTypes; |
| 1746 | + |
| 1747 | + // We only care about constraints on Expression* things. |
| 1748 | + void noteSubtype(Type sub, Type super) {} |
| 1749 | + void noteSubtype(HeapType sub, HeapType super) {} |
| 1750 | + |
| 1751 | + void noteSubtype(Type sub, Expression* super) { |
| 1752 | + // The expression must be a supertype of a fixed type. Nothing to do. |
| 1753 | + } |
| 1754 | + void noteSubtype(Expression* sub, Type super) { |
| 1755 | + if (super.isRef() && sub->type != super) { |
| 1756 | + // This is a nontrivial opportunity to replace sub with a given type. |
| 1757 | + childTypes[sub] = super; |
| 1758 | + } |
| 1759 | + } |
| 1760 | + void noteSubtype(Expression* sub, Expression* super) { |
| 1761 | + noteSubtype(sub, super->type); |
| 1762 | + } |
| 1763 | + void noteNonFlowSubtype(Expression* sub, Type super) { |
| 1764 | + noteSubtype(sub, super); |
| 1765 | + } |
| 1766 | + |
| 1767 | + // TODO: Many casts can accept the top type. We may need to use visit*(), to |
| 1768 | + // handle each expression class separately. |
| 1769 | + void noteCast(HeapType src, HeapType dst) {} |
| 1770 | + void noteCast(Expression* src, Type dst) {} |
| 1771 | + void noteCast(Expression* src, Expression* dst) {} |
| 1772 | + } finder; |
| 1773 | + |
| 1774 | + if (oneIn(2)) { |
| 1775 | + // |finder| reads the IR, and it must be totally valid - e.g. breaks have a |
| 1776 | + // proper break target - or else we'd hit internal errors. Fix it up first. |
| 1777 | + // (Otherwise, fixing it up is done once after mutation, and in that case |
| 1778 | + // we can mutate the IR in simple ways but not read it using |
| 1779 | + // useSubtypingDiscoverer). We avoid always doing this second fixup as it |
| 1780 | + // may bias the code in some ways. |
| 1781 | + fixAfterChanges(func); |
| 1782 | + finder.walkFunctionInModule(func, &wasm); |
| 1783 | + } |
| 1784 | + |
| 1785 | + // Next, modify things. |
1736 | 1786 | struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { |
1737 | 1787 | TranslateToFuzzReader& parent; |
1738 | 1788 | Index percentChance; |
| 1789 | + Finder& finder; |
1739 | 1790 |
|
1740 | 1791 | // Whether to replace with unreachable. This can lead to less code getting |
1741 | 1792 | // executed, so we don't want to do it all the time even in a big function. |
1742 | 1793 | bool allowUnreachable; |
1743 | 1794 |
|
1744 | | - Modder(TranslateToFuzzReader& parent, Index percentChance) |
1745 | | - : parent(parent), percentChance(percentChance) { |
| 1795 | + Modder(TranslateToFuzzReader& parent, Index percentChance, Finder& finder) |
| 1796 | + : parent(parent), percentChance(percentChance), finder(finder) { |
1746 | 1797 | // If the parent allows it then sometimes replace with an unreachable, and |
1747 | 1798 | // sometimes not. Even if we allow it, only do it in certain functions |
1748 | 1799 | // (half the time) and only do it rarely (see below). |
1749 | 1800 | allowUnreachable = parent.allowAddingUnreachableCode && parent.oneIn(2); |
1750 | 1801 | } |
1751 | 1802 |
|
1752 | 1803 | void visitExpression(Expression* curr) { |
1753 | | - if (parent.upTo(100) < percentChance && |
1754 | | - parent.canBeArbitrarilyReplaced(curr)) { |
1755 | | - // We can replace in various modes, see below. Generate a random number |
1756 | | - // up to 100 to help us there. |
1757 | | - int mode = parent.upTo(100); |
1758 | | - |
1759 | | - if (allowUnreachable && mode < 5) { |
1760 | | - replaceCurrent(parent.make(Type::unreachable)); |
1761 | | - return; |
| 1804 | + // See if we want to replace it. |
| 1805 | + if (!parent.canBeArbitrarilyReplaced(curr) || |
| 1806 | + parent.upTo(100) >= percentChance) { |
| 1807 | + return; |
| 1808 | + } |
| 1809 | + |
| 1810 | + // Find the type to replace with. |
| 1811 | + auto type = curr->type; |
| 1812 | + if (type.isRef()) { |
| 1813 | + auto iter = finder.childTypes.find(curr); |
| 1814 | + if (iter != finder.childTypes.end()) { |
| 1815 | + type = iter->second; |
| 1816 | + // We can only be given a less-refined type (certainly we can replace |
| 1817 | + // curr with its own type). |
| 1818 | + assert(Type::isSubType(curr->type, type)); |
| 1819 | + // We only store an interesting non-trivial type. |
| 1820 | + assert(type != curr->type); |
1762 | 1821 | } |
| 1822 | + } |
1763 | 1823 |
|
1764 | | - // For constants, perform only a small tweaking in some cases. |
1765 | | - // TODO: more minor tweaks to immediates, like making a load atomic or |
1766 | | - // not, changing an offset, etc. |
1767 | | - if (auto* c = curr->dynCast<Const>()) { |
1768 | | - if (mode < 50) { |
1769 | | - c->value = parent.tweak(c->value); |
1770 | | - } else { |
1771 | | - // Just replace the entire thing. |
1772 | | - replaceCurrent(parent.make(curr->type)); |
1773 | | - } |
1774 | | - return; |
| 1824 | + // We can replace in various modes, see below. Generate a random number |
| 1825 | + // up to 100 to help us there. |
| 1826 | + int mode = parent.upTo(100); |
| 1827 | + |
| 1828 | + if (allowUnreachable && mode < 5) { |
| 1829 | + replaceCurrent(parent.make(Type::unreachable)); |
| 1830 | + return; |
| 1831 | + } |
| 1832 | + |
| 1833 | + // For constants, perform only a small tweaking in some cases. |
| 1834 | + // TODO: more minor tweaks to immediates, like making a load atomic or |
| 1835 | + // not, changing an offset, etc. |
| 1836 | + if (auto* c = curr->dynCast<Const>()) { |
| 1837 | + if (mode < 50) { |
| 1838 | + c->value = parent.tweak(c->value); |
| 1839 | + } else { |
| 1840 | + // Just replace the entire thing. |
| 1841 | + replaceCurrent(parent.make(type)); |
1775 | 1842 | } |
| 1843 | + return; |
| 1844 | + } |
1776 | 1845 |
|
1777 | | - // Generate a replacement for the expression, and by default replace all |
1778 | | - // of |curr| (including children) with that replacement, but in some |
1779 | | - // cases we can do more subtle things. |
| 1846 | + // Generate a replacement for the expression, and by default replace all |
| 1847 | + // of |curr| (including children) with that replacement, but in some |
| 1848 | + // cases we can do more subtle things. |
| 1849 | + // |
| 1850 | + // Note that such a replacement is not always valid due to nesting of |
| 1851 | + // labels, but we'll fix that up later. Note also that make() picks a |
| 1852 | + // subtype, so this has a chance to replace us with anything that is |
| 1853 | + // valid to put here. |
| 1854 | + auto* rep = parent.make(type); |
| 1855 | + if (mode < 33 && rep->type != Type::none) { |
| 1856 | + // This has a non-none type. Replace the output, keeping the |
| 1857 | + // expression and its children in a drop. This "interposes" between |
| 1858 | + // this expression and its parent, something like this: |
1780 | 1859 | // |
1781 | | - // Note that such a replacement is not always valid due to nesting of |
1782 | | - // labels, but we'll fix that up later. Note also that make() picks a |
1783 | | - // subtype, so this has a chance to replace us with anything that is |
1784 | | - // valid to put here. |
1785 | | - auto* rep = parent.make(curr->type); |
1786 | | - if (mode < 33 && rep->type != Type::none) { |
1787 | | - // This has a non-none type. Replace the output, keeping the |
1788 | | - // expression and its children in a drop. This "interposes" between |
1789 | | - // this expression and its parent, something like this: |
1790 | | - // |
1791 | | - // (D |
1792 | | - // (A |
1793 | | - // (B) |
1794 | | - // (C) |
1795 | | - // ) |
1796 | | - // ) |
1797 | | - //// |
1798 | | - // => ;; keep A, replace it in the parent |
| 1860 | + // (D |
| 1861 | + // (A |
| 1862 | + // (B) |
| 1863 | + // (C) |
| 1864 | + // ) |
| 1865 | + // ) |
| 1866 | + //// |
| 1867 | + // => ;; keep A, replace it in the parent |
| 1868 | + // |
| 1869 | + // (D |
| 1870 | + // (block |
| 1871 | + // (drop |
| 1872 | + // (A |
| 1873 | + // (B) |
| 1874 | + // (C) |
| 1875 | + // ) |
| 1876 | + // ) |
| 1877 | + // (NEW) |
| 1878 | + // ) |
| 1879 | + // ) |
| 1880 | + // |
| 1881 | + // We also sometimes try to insert A as a child of NEW, so we actually |
| 1882 | + // interpose directly: |
| 1883 | + // |
| 1884 | + // (D |
| 1885 | + // (NEW |
| 1886 | + // (A |
| 1887 | + // (B) |
| 1888 | + // (C) |
| 1889 | + // ) |
| 1890 | + // ) |
| 1891 | + // ) |
| 1892 | + // |
| 1893 | + // We do not do that all the time, as inserting a drop is actually an |
| 1894 | + // important situation to test: the drop makes the output of A unused, |
| 1895 | + // which may let optimizations remove it. |
| 1896 | + if ((mode & 1) && replaceChildWith(rep, curr)) { |
| 1897 | + // We managed to replace one of the children with curr, and have |
| 1898 | + // nothing more to do. |
| 1899 | + } else { |
| 1900 | + // Drop curr and append. |
| 1901 | + rep = parent.builder.makeSequence(parent.builder.makeDrop(curr), rep); |
| 1902 | + } |
| 1903 | + } else if (mode >= 66 && !Properties::isControlFlowStructure(curr)) { |
| 1904 | + ChildIterator children(curr); |
| 1905 | + auto numChildren = children.getNumChildren(); |
| 1906 | + if (numChildren > 0 && numChildren < 5) { |
| 1907 | + // This is a normal (non-control-flow) expression with at least one |
| 1908 | + // child (and not an excessive amount of them; see the processing |
| 1909 | + // below). "Interpose" between the children and this expression by |
| 1910 | + // keeping them and replacing the parent |curr|. We do this by |
| 1911 | + // generating drops of the children, like this: |
1799 | 1912 | // |
1800 | | - // (D |
1801 | | - // (block |
1802 | | - // (drop |
1803 | | - // (A |
1804 | | - // (B) |
1805 | | - // (C) |
1806 | | - // ) |
1807 | | - // ) |
1808 | | - // (NEW) |
1809 | | - // ) |
1810 | | - // ) |
| 1913 | + // (A |
| 1914 | + // (B) |
| 1915 | + // (C) |
| 1916 | + // ) |
1811 | 1917 | // |
1812 | | - // We also sometimes try to insert A as a child of NEW, so we actually |
1813 | | - // interpose directly: |
| 1918 | + // => ;; keep children, replace A |
1814 | 1919 | // |
1815 | | - // (D |
1816 | | - // (NEW |
1817 | | - // (A |
1818 | | - // (B) |
1819 | | - // (C) |
1820 | | - // ) |
1821 | | - // ) |
1822 | | - // ) |
| 1920 | + // (block |
| 1921 | + // (drop (B)) |
| 1922 | + // (drop (C)) |
| 1923 | + // (NEW) |
| 1924 | + // ) |
1823 | 1925 | // |
1824 | | - // We do not do that all the time, as inserting a drop is actually an |
1825 | | - // important situation to test: the drop makes the output of A unused, |
1826 | | - // which may let optimizations remove it. |
1827 | | - if ((mode & 1) && replaceChildWith(rep, curr)) { |
1828 | | - // We managed to replace one of the children with curr, and have |
1829 | | - // nothing more to do. |
1830 | | - } else { |
1831 | | - // Drop curr and append. |
1832 | | - rep = |
1833 | | - parent.builder.makeSequence(parent.builder.makeDrop(curr), rep); |
1834 | | - } |
1835 | | - } else if (mode >= 66 && !Properties::isControlFlowStructure(curr)) { |
1836 | | - ChildIterator children(curr); |
1837 | | - auto numChildren = children.getNumChildren(); |
1838 | | - if (numChildren > 0 && numChildren < 5) { |
1839 | | - // This is a normal (non-control-flow) expression with at least one |
1840 | | - // child (and not an excessive amount of them; see the processing |
1841 | | - // below). "Interpose" between the children and this expression by |
1842 | | - // keeping them and replacing the parent |curr|. We do this by |
1843 | | - // generating drops of the children, like this: |
1844 | | - // |
1845 | | - // (A |
1846 | | - // (B) |
1847 | | - // (C) |
1848 | | - // ) |
1849 | | - // |
1850 | | - // => ;; keep children, replace A |
1851 | | - // |
1852 | | - // (block |
1853 | | - // (drop (B)) |
1854 | | - // (drop (C)) |
1855 | | - // (NEW) |
1856 | | - // ) |
1857 | | - // |
1858 | | - auto* block = parent.builder.makeBlock(); |
1859 | | - for (auto* child : children) { |
1860 | | - // Only drop the child if we can't replace it as one of NEW's |
1861 | | - // children. This does a linear scan of |rep| which is the reason |
1862 | | - // for the above limit on the number of children. |
1863 | | - if (!replaceChildWith(rep, child)) { |
1864 | | - block->list.push_back(parent.builder.makeDrop(child)); |
1865 | | - } |
| 1926 | + auto* block = parent.builder.makeBlock(); |
| 1927 | + for (auto* child : children) { |
| 1928 | + // Only drop the child if we can't replace it as one of NEW's |
| 1929 | + // children. This does a linear scan of |rep| which is the reason |
| 1930 | + // for the above limit on the number of children. |
| 1931 | + if (!replaceChildWith(rep, child)) { |
| 1932 | + block->list.push_back(parent.builder.makeDrop(child)); |
1866 | 1933 | } |
| 1934 | + } |
1867 | 1935 |
|
1868 | | - if (!block->list.empty()) { |
1869 | | - // We need the block, that is, we did not find a place for all the |
1870 | | - // children. |
1871 | | - block->list.push_back(rep); |
1872 | | - block->finalize(); |
1873 | | - rep = block; |
1874 | | - } |
| 1936 | + if (!block->list.empty()) { |
| 1937 | + // We need the block, that is, we did not find a place for all the |
| 1938 | + // children. |
| 1939 | + block->list.push_back(rep); |
| 1940 | + block->finalize(); |
| 1941 | + rep = block; |
1875 | 1942 | } |
1876 | 1943 | } |
1877 | | - replaceCurrent(rep); |
1878 | 1944 | } |
| 1945 | + replaceCurrent(rep); |
1879 | 1946 | } |
1880 | 1947 | }; |
1881 | 1948 |
|
1882 | | - Modder modder(*this, percentChance); |
| 1949 | + Modder modder(*this, percentChance, finder); |
1883 | 1950 | modder.walkFunctionInModule(func, &wasm); |
1884 | 1951 | } |
1885 | 1952 |
|
|
0 commit comments