Skip to content

Commit 5c444ad

Browse files
committed
Merge remote-tracking branch 'origin/master' into master-next
2 parents 081b314 + 6948319 commit 5c444ad

File tree

4 files changed

+667
-1
lines changed

4 files changed

+667
-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)