|
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