Skip to content

Commit 5298f81

Browse files
committed
SimplifyCFG: Simplify switch_enum on optional classes used by objc method calls
In the statement optional1?.objc_setter = optional2?.objc_getter?.objc_getter we can eliminate all optional switches expect for the first switch on optional1. We must only execute the setter if optional1 has some value. We can simplify the following switch_enum with a branch as long all sideffecting instructions in someBB are objc_method calls on the optional payload or on another objc_method call that transitively uses the payload. switch_enum %optionalValue, case #Optional.some!enumelt.1: someBB, case #Optional.none: noneBB someBB(%optionalPayload): %1 = objc_method %optionalPayload %2 = apply %1(..., %optionalPayload) // self position br mergeBB(%2) noneBB: %4 = enum #Optional.none br mergeBB(%4) rdar://48007302
1 parent 7ca542b commit 5298f81

File tree

4 files changed

+664
-1
lines changed

4 files changed

+664
-1
lines changed

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 210 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ namespace {
192192
bool simplifyArgument(SILBasicBlock *BB, unsigned i);
193193
bool simplifyArgs(SILBasicBlock *BB);
194194
void findLoopHeaders();
195+
bool simplifySwitchEnumOnObjcClassOptional(SwitchEnumInst *SEI);
195196
};
196197

197198
} // end anonymous namespace
@@ -1731,6 +1732,212 @@ bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) {
17311732
return true;
17321733
}
17331734

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+
17341941
/// simplifySwitchEnumBlock - Simplify a basic block that ends with a
17351942
/// switch_enum instruction that gets its operand from an enum
17361943
/// instruction.
@@ -2316,7 +2523,9 @@ bool SimplifyCFG::simplifyBlocks() {
23162523
case TermKind::SwitchEnumInst: {
23172524
auto *SEI = cast<SwitchEnumInst>(TI);
23182525
if (simplifySwitchEnumBlock(SEI)) {
2319-
Changed = false;
2526+
Changed = true;
2527+
} else if (simplifySwitchEnumOnObjcClassOptional(SEI)) {
2528+
Changed = true;
23202529
} else {
23212530
Changed |= simplifySwitchEnumUnreachableBlocks(SEI);
23222531
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import Foundation;
2+
3+
@interface Test : NSObject
4+
@property (nullable) Test *otherTest;
5+
@end

0 commit comments

Comments
 (0)