|
15 | 15 | //===----------------------------------------------------------------------===// |
16 | 16 |
|
17 | 17 | #include "swift/SIL/SILInstruction.h" |
18 | | -#include "swift/Basic/Assertions.h" |
19 | 18 | #include "swift/Basic/AssertImplements.h" |
| 19 | +#include "swift/Basic/Assertions.h" |
20 | 20 | #include "swift/Basic/Unicode.h" |
21 | 21 | #include "swift/Basic/type_traits.h" |
22 | 22 | #include "swift/SIL/ApplySite.h" |
23 | 23 | #include "swift/SIL/DynamicCasts.h" |
| 24 | +#include "swift/SIL/InstWrappers.h" |
24 | 25 | #include "swift/SIL/InstructionUtils.h" |
| 26 | +#include "swift/SIL/NodeDatastructures.h" |
25 | 27 | #include "swift/SIL/OwnershipUtils.h" |
| 28 | +#include "swift/SIL/PrunedLiveness.h" |
26 | 29 | #include "swift/SIL/SILBuilder.h" |
27 | 30 | #include "swift/SIL/SILCloner.h" |
28 | 31 | #include "swift/SIL/SILDebugScope.h" |
@@ -1828,6 +1831,134 @@ bool SILInstruction::maySuspend() const { |
1828 | 1831 | return false; |
1829 | 1832 | } |
1830 | 1833 |
|
| 1834 | +static SILValue lookThroughOwnershipAndForwardingInsts(SILValue value) { |
| 1835 | + auto current = value; |
| 1836 | + while (true) { |
| 1837 | + if (auto *inst = current->getDefiningInstruction()) { |
| 1838 | + switch (inst->getKind()) { |
| 1839 | + case SILInstructionKind::MoveValueInst: |
| 1840 | + case SILInstructionKind::CopyValueInst: |
| 1841 | + case SILInstructionKind::BeginBorrowInst: |
| 1842 | + current = inst->getOperand(0); |
| 1843 | + continue; |
| 1844 | + default: |
| 1845 | + break; |
| 1846 | + } |
| 1847 | + auto forward = ForwardingOperation(inst); |
| 1848 | + Operand *op = nullptr; |
| 1849 | + if (forward && (op = forward.getSingleForwardingOperand())) { |
| 1850 | + current = op->get(); |
| 1851 | + continue; |
| 1852 | + } |
| 1853 | + } else if (auto *result = SILArgument::isTerminatorResult(current)) { |
| 1854 | + auto *op = result->forwardedTerminatorResultOperand(); |
| 1855 | + if (!op) { |
| 1856 | + break; |
| 1857 | + } |
| 1858 | + current = op->get(); |
| 1859 | + continue; |
| 1860 | + } |
| 1861 | + break; |
| 1862 | + } |
| 1863 | + return current; |
| 1864 | +} |
| 1865 | + |
| 1866 | +bool |
| 1867 | +PartialApplyInst::visitOnStackLifetimeEnds( |
| 1868 | + llvm::function_ref<bool (Operand *)> func) const { |
| 1869 | + assert(getFunction()->hasOwnership() |
| 1870 | + && isOnStack() |
| 1871 | + && "only meaningful for OSSA stack closures"); |
| 1872 | + bool noUsers = true; |
| 1873 | + |
| 1874 | + auto *function = getFunction(); |
| 1875 | + |
| 1876 | + SmallVector<SILBasicBlock *, 32> discoveredBlocks; |
| 1877 | + SSAPrunedLiveness liveness(function, &discoveredBlocks); |
| 1878 | + liveness.initializeDef(this); |
| 1879 | + |
| 1880 | + StackList<SILValue> values(function); |
| 1881 | + values.push_back(this); |
| 1882 | + |
| 1883 | + while (!values.empty()) { |
| 1884 | + auto value = values.pop_back_val(); |
| 1885 | + for (auto *use : value->getUses()) { |
| 1886 | + if (!use->isConsuming()) { |
| 1887 | + if (auto *cvi = dyn_cast<CopyValueInst>(use->getUser())) { |
| 1888 | + values.push_back(cvi); |
| 1889 | + } |
| 1890 | + continue; |
| 1891 | + } |
| 1892 | + noUsers = false; |
| 1893 | + if (isa<DestroyValueInst>(use->getUser())) { |
| 1894 | + liveness.updateForUse(use->getUser(), /*lifetimeEnding=*/true); |
| 1895 | + continue; |
| 1896 | + } |
| 1897 | + auto forward = ForwardingOperand(use); |
| 1898 | + if (!forward) { |
| 1899 | + // There shouldn't be any non-forwarding consumptions of a nonescaping |
| 1900 | + // partial_apply that don't forward it along, aside from destroy_value. |
| 1901 | + // |
| 1902 | + // On-stack partial_apply cannot be cloned, so it should never be used |
| 1903 | + // by a BranchInst. |
| 1904 | + // |
| 1905 | + // This is a fatal error because it performs SIL verification that is |
| 1906 | + // not separately checked in the verifier. It is the only check that |
| 1907 | + // verifies the structural requirements of on-stack partial_apply uses. |
| 1908 | + if (lookThroughOwnershipAndForwardingInsts(use->get()) != |
| 1909 | + SILValue(this)) { |
| 1910 | + // Consumes of values which aren't "essentially" the |
| 1911 | + // partial_apply [on_stack] |
| 1912 | + // are okay. For example, a not-on_stack partial_apply that captures |
| 1913 | + // it. |
| 1914 | + continue; |
| 1915 | + } |
| 1916 | + llvm::errs() << "partial_apply [on_stack] use:\n"; |
| 1917 | + auto *user = use->getUser(); |
| 1918 | + user->printInContext(llvm::errs()); |
| 1919 | + if (isa<BranchInst>(user)) { |
| 1920 | + llvm::report_fatal_error("partial_apply [on_stack] cannot be cloned"); |
| 1921 | + } |
| 1922 | + llvm::report_fatal_error("partial_apply [on_stack] must be directly " |
| 1923 | + "forwarded to a destroy_value"); |
| 1924 | + } |
| 1925 | + forward.visitForwardedValues([&values](auto value) { |
| 1926 | + values.push_back(value); |
| 1927 | + return true; |
| 1928 | + }); |
| 1929 | + } |
| 1930 | + } |
| 1931 | + PrunedLivenessBoundary boundary; |
| 1932 | + liveness.computeBoundary(boundary); |
| 1933 | + |
| 1934 | + for (auto *inst : boundary.lastUsers) { |
| 1935 | + // Only destroy_values were added to liveness, so only destroy_values can be |
| 1936 | + // the last users. |
| 1937 | + auto *dvi = cast<DestroyValueInst>(inst); |
| 1938 | + auto keepGoing = func(&dvi->getOperandRef()); |
| 1939 | + if (!keepGoing) { |
| 1940 | + return false; |
| 1941 | + } |
| 1942 | + } |
| 1943 | + return !noUsers; |
| 1944 | +} |
| 1945 | + |
| 1946 | +namespace swift::test { |
| 1947 | +FunctionTest PartialApplyPrintOnStackLifetimeEnds( |
| 1948 | + "partial_apply_print_on_stack_lifetime_ends", |
| 1949 | + [](auto &function, auto &arguments, auto &test) { |
| 1950 | + auto *inst = arguments.takeInstruction(); |
| 1951 | + auto *pai = cast<PartialApplyInst>(inst); |
| 1952 | + function.print(llvm::outs()); |
| 1953 | + auto result = pai->visitOnStackLifetimeEnds([](auto *operand) { |
| 1954 | + operand->print(llvm::outs()); |
| 1955 | + return true; |
| 1956 | + }); |
| 1957 | + const char *resultString = result ? "true" : "false"; |
| 1958 | + llvm::outs() << "returned: " << resultString << "\n"; |
| 1959 | + }); |
| 1960 | +} // end namespace swift::test |
| 1961 | + |
1831 | 1962 | static bool |
1832 | 1963 | visitRecursivelyLifetimeEndingUses( |
1833 | 1964 | SILValue i, bool &noUsers, |
@@ -1869,41 +2000,6 @@ visitRecursivelyLifetimeEndingUses( |
1869 | 2000 | return true; |
1870 | 2001 | } |
1871 | 2002 |
|
1872 | | -bool |
1873 | | -PartialApplyInst::visitOnStackLifetimeEnds( |
1874 | | - llvm::function_ref<bool (Operand *)> func) const { |
1875 | | - assert(getFunction()->hasOwnership() |
1876 | | - && isOnStack() |
1877 | | - && "only meaningful for OSSA stack closures"); |
1878 | | - bool noUsers = true; |
1879 | | - |
1880 | | - auto visitUnknownUse = [](Operand *unknownUse) { |
1881 | | - // There shouldn't be any dead-end consumptions of a nonescaping |
1882 | | - // partial_apply that don't forward it along, aside from destroy_value. |
1883 | | - // |
1884 | | - // On-stack partial_apply cannot be cloned, so it should never be used by a |
1885 | | - // BranchInst. |
1886 | | - // |
1887 | | - // This is a fatal error because it performs SIL verification that is not |
1888 | | - // separately checked in the verifier. It is the only check that verifies |
1889 | | - // the structural requirements of on-stack partial_apply uses. |
1890 | | - llvm::errs() << "partial_apply [on_stack] use:\n"; |
1891 | | - auto *user = unknownUse->getUser(); |
1892 | | - user->printInContext(llvm::errs()); |
1893 | | - if (isa<BranchInst>(user)) { |
1894 | | - llvm::report_fatal_error("partial_apply [on_stack] cannot be cloned"); |
1895 | | - } |
1896 | | - llvm::report_fatal_error("partial_apply [on_stack] must be directly " |
1897 | | - "forwarded to a destroy_value"); |
1898 | | - return false; |
1899 | | - }; |
1900 | | - if (!visitRecursivelyLifetimeEndingUses(this, noUsers, func, |
1901 | | - visitUnknownUse)) { |
1902 | | - return false; |
1903 | | - } |
1904 | | - return !noUsers; |
1905 | | -} |
1906 | | - |
1907 | 2003 | // FIXME: Rather than recursing through all results, this should only recurse |
1908 | 2004 | // through ForwardingInstruction and OwnershipTransitionInstruction and the |
1909 | 2005 | // client should prove that any other uses cannot be upstream from a consume of |
|
0 commit comments