|
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