@@ -192,6 +192,7 @@ namespace {
192
192
bool simplifyArgument (SILBasicBlock *BB, unsigned i);
193
193
bool simplifyArgs (SILBasicBlock *BB);
194
194
void findLoopHeaders ();
195
+ bool simplifySwitchEnumOnObjcClassOptional (SwitchEnumInst *SEI);
195
196
};
196
197
197
198
} // end anonymous namespace
@@ -1731,6 +1732,212 @@ bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) {
1731
1732
return true ;
1732
1733
}
1733
1734
1735
+ // / Checks that the someBB only contains obj_method calls (possibly chained) on
1736
+ // / the optional value.
1737
+ // /
1738
+ // / switch_enum %optionalValue, case #Optional.some!enumelt.1: someBB
1739
+ // /
1740
+ // / someBB(%optionalPayload):
1741
+ // / %1 = objc_method %optionalPayload
1742
+ // / %2 = apply %1(..., %optionalPayload) // self position
1743
+ // / %3 = unchecked_ref_cast %2
1744
+ // / %4 = objc_method %3
1745
+ // / %... = apply %4(..., %3)
1746
+ // / br mergeBB(%...)
1747
+ static bool containsOnlyObjMethodCallOnOptional (SILValue optionalValue,
1748
+ SILBasicBlock *someBB,
1749
+ SILValue &outBranchArg,
1750
+ SILValue &outOptionalPayload) {
1751
+ SILValue optionalPayload;
1752
+ SmallVector<SILValue, 4 > optionalPayloads;
1753
+ if (someBB->getNumArguments () == 1 ) {
1754
+ optionalPayload = someBB->getArgument (0 );
1755
+ optionalPayloads.push_back (optionalPayload);
1756
+ } else if (someBB->getNumArguments () != 0 )
1757
+ return false ;
1758
+
1759
+ SmallVector<SILValue, 4 > objCApplies;
1760
+ for (auto &i : *someBB) {
1761
+ SILInstruction *inst = &i;
1762
+ if (onlyAffectsRefCount (inst))
1763
+ continue ;
1764
+ if (inst->isDebugInstruction ())
1765
+ continue ;
1766
+ // An objc_method has no sideffects.
1767
+ if (isa<ObjCMethodInst>(inst))
1768
+ continue ;
1769
+
1770
+ // An uncheckedEnumData has no sideeffects.
1771
+ if (auto *uncheckedEnumData = dyn_cast<UncheckedEnumDataInst>(inst)) {
1772
+ if (uncheckedEnumData->getOperand () != optionalValue)
1773
+ continue ;
1774
+ optionalPayload = uncheckedEnumData;
1775
+ optionalPayloads.push_back (uncheckedEnumData);
1776
+ continue ;
1777
+ }
1778
+
1779
+ // An unchecked_ref_cast is safe.
1780
+ if (auto *refCast = dyn_cast<UncheckedRefCastInst>(inst)) {
1781
+ // An unchecked_ref_cast on a safe objc_method apply behaves like the
1782
+ // optional (it is null if the optional was null).
1783
+ if (refCast->getType ().getClassOrBoundGenericClass () &&
1784
+ std::find (objCApplies.begin (), objCApplies.end (),
1785
+ refCast->getOperand ()) != objCApplies.end ())
1786
+ optionalPayloads.push_back (refCast);
1787
+ continue ;
1788
+ }
1789
+
1790
+ // Applies on objc_methods where self is either the optional payload or the
1791
+ // result of another 'safe' apply are safe.
1792
+ if (auto *objcMethod = dyn_cast<ApplyInst>(inst)) {
1793
+ if (!isa<ObjCMethodInst>(objcMethod->getCallee ()))
1794
+ return false ;
1795
+ if (std::find (optionalPayloads.begin (), optionalPayloads.end (),
1796
+ objcMethod->getSelfArgument ()) == optionalPayloads.end ())
1797
+ return false ;
1798
+ objCApplies.push_back (objcMethod);
1799
+ continue ;
1800
+ }
1801
+
1802
+ // The branch should forward one of the objc_method call.
1803
+ if (auto *br = dyn_cast<BranchInst>(inst)) {
1804
+ if (br->getNumArgs () == 0 || br->getNumArgs () > 1 )
1805
+ return false ;
1806
+ auto branchArg = br->getArg (0 );
1807
+ if (std::find (objCApplies.begin (), objCApplies.end (), branchArg) ==
1808
+ objCApplies.end ())
1809
+ return false ;
1810
+ outBranchArg = branchArg;
1811
+ continue ;
1812
+ }
1813
+ // Unexpected instruction.
1814
+ return false ;
1815
+ }
1816
+ if (!optionalPayload)
1817
+ return false ;
1818
+ outOptionalPayload = optionalPayload;
1819
+ return true ;
1820
+ }
1821
+
1822
+ // / Check that all that noneBB does is forwarding none.
1823
+ // / The only other allowed operation are ref count operations.
1824
+ static bool onlyForwardsNone (SILBasicBlock *noneBB, SILBasicBlock *someBB,
1825
+ SwitchEnumInst *SEI) {
1826
+ // It all the basic blocks leading up to the ultimate block we only expect
1827
+ // reference count instructions.
1828
+ while (noneBB->getSingleSuccessorBlock () != someBB->getSingleSuccessorBlock ()) {
1829
+ for (auto &i : *noneBB) {
1830
+ auto *inst = &i;
1831
+ if (isa<BranchInst>(inst) || onlyAffectsRefCount (inst) ||
1832
+ inst->isDebugInstruction ())
1833
+ continue ;
1834
+ return false ;
1835
+ }
1836
+ noneBB = noneBB->getSingleSuccessorBlock ();
1837
+ }
1838
+ // The ultimate block forwards the Optional<...>.none value.
1839
+ SILValue optionalNone;
1840
+ for (auto &i : *noneBB) {
1841
+ auto *inst = &i;
1842
+ if (onlyAffectsRefCount (inst) || inst->isDebugInstruction ())
1843
+ continue ;
1844
+ if (auto *none = dyn_cast<EnumInst>(inst)) {
1845
+ if (none->getElement () !=
1846
+ SEI->getModule ().getASTContext ().getOptionalNoneDecl ())
1847
+ return false ;
1848
+ optionalNone = none;
1849
+ continue ;
1850
+ }
1851
+ if (auto *noneBranch = dyn_cast<BranchInst>(inst)) {
1852
+ if (noneBranch->getNumArgs () != 1 ||
1853
+ (noneBranch->getArg (0 ) != SEI->getOperand () &&
1854
+ noneBranch->getArg (0 ) != optionalNone))
1855
+ return false ;
1856
+ continue ;
1857
+ }
1858
+ return false ;
1859
+ }
1860
+ return true ;
1861
+ }
1862
+
1863
+ // / Check whether \p noneBB has the same ultimate successor as the successor to someBB.
1864
+ // / someBB noneBB
1865
+ // / \ |
1866
+ // / \ ... (more bbs?)
1867
+ // / \ /
1868
+ // / ulimateBB
1869
+ static bool hasSameUlitmateSuccessor (SILBasicBlock *noneBB, SILBasicBlock *someBB) {
1870
+ auto *someSuccessorBB = someBB->getSingleSuccessorBlock ();
1871
+ if (!someSuccessorBB)
1872
+ return false ;
1873
+ auto *noneSuccessorBB = noneBB->getSingleSuccessorBlock ();
1874
+ while (noneSuccessorBB != nullptr && noneSuccessorBB != someSuccessorBB)
1875
+ noneSuccessorBB = noneSuccessorBB->getSingleSuccessorBlock ();
1876
+ return noneSuccessorBB == someSuccessorBB;
1877
+ }
1878
+
1879
+ // / Simplify switch_enums on class enums that branch to objc_method calls on
1880
+ // / that optional on the #Optional.some side to always branch to the some side.
1881
+ // /
1882
+ // / switch_enum %optionalValue, case #Optional.some!enumelt.1: someBB,
1883
+ // / case #Optional.none: noneBB
1884
+ // /
1885
+ // / someBB(%optionalPayload):
1886
+ // / %1 = objc_method %optionalPayload
1887
+ // / %2 = apply %1(..., %optionalPayload) // self position
1888
+ // / br mergeBB(%2)
1889
+ // /
1890
+ // / noneBB:
1891
+ // / %4 = enum #Optional.none
1892
+ // / br mergeBB(%4)
1893
+ bool SimplifyCFG::simplifySwitchEnumOnObjcClassOptional (SwitchEnumInst *SEI) {
1894
+ auto optional = SEI->getOperand ();
1895
+ auto optionalPayloadType = optional->getType ().getOptionalObjectType ();
1896
+ if (!optionalPayloadType ||
1897
+ !optionalPayloadType.getClassOrBoundGenericClass ())
1898
+ return false ;
1899
+
1900
+ if (SEI->getNumCases () != 2 )
1901
+ return false ;
1902
+
1903
+ auto *noneBB = SEI->getCase (0 ).second ;
1904
+ auto *someBB = SEI->getCase (1 ).second ;
1905
+ if (noneBB == someBB)
1906
+ return false ;
1907
+ auto someDecl = SEI->getModule ().getASTContext ().getOptionalSomeDecl ();
1908
+ if (SEI->getCaseDestination (someDecl) != someBB)
1909
+ std::swap (someBB, noneBB);
1910
+
1911
+ if (!hasSameUlitmateSuccessor (noneBB, someBB))
1912
+ return false ;
1913
+
1914
+ if (!onlyForwardsNone (noneBB, someBB, SEI))
1915
+ return false ;
1916
+
1917
+ SILValue branchArg;
1918
+ SILValue optionalPayload;
1919
+ if (!containsOnlyObjMethodCallOnOptional (optional, someBB, branchArg,
1920
+ optionalPayload))
1921
+ return false ;
1922
+
1923
+ SILBuilderWithScope Builder (SEI);
1924
+ auto *payloadCast = Builder.createUncheckedRefCast (SEI->getLoc (), optional,
1925
+ optionalPayloadType);
1926
+ optionalPayload->replaceAllUsesWith (payloadCast);
1927
+ auto *switchBB = SEI->getParent ();
1928
+ if (someBB->getNumArguments ())
1929
+ Builder.createBranch (SEI->getLoc (), someBB, SILValue (payloadCast));
1930
+ else
1931
+ Builder.createBranch (SEI->getLoc (), someBB);
1932
+
1933
+ SEI->eraseFromParent ();
1934
+ addToWorklist (switchBB);
1935
+ simplifyAfterDroppingPredecessor (noneBB);
1936
+ addToWorklist (someBB);
1937
+ ++NumConstantFolded;
1938
+ return true ;
1939
+ }
1940
+
1734
1941
// / simplifySwitchEnumBlock - Simplify a basic block that ends with a
1735
1942
// / switch_enum instruction that gets its operand from an enum
1736
1943
// / instruction.
@@ -2316,7 +2523,9 @@ bool SimplifyCFG::simplifyBlocks() {
2316
2523
case TermKind::SwitchEnumInst: {
2317
2524
auto *SEI = cast<SwitchEnumInst>(TI);
2318
2525
if (simplifySwitchEnumBlock (SEI)) {
2319
- Changed = false ;
2526
+ Changed = true ;
2527
+ } else if (simplifySwitchEnumOnObjcClassOptional (SEI)) {
2528
+ Changed = true ;
2320
2529
} else {
2321
2530
Changed |= simplifySwitchEnumUnreachableBlocks (SEI);
2322
2531
}
0 commit comments