Skip to content

Commit e3fcc0d

Browse files
committed
[NFC] Work in preparation of generalizing how we combine conversions
1 parent ae4dc77 commit e3fcc0d

File tree

2 files changed

+169
-137
lines changed

2 files changed

+169
-137
lines changed

lib/SILGen/Conversion.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,9 @@ class ConversionPeepholeHint {
294294
bool isForced() const { return Forced; }
295295
};
296296

297-
std::optional<ConversionPeepholeHint>
298-
canPeepholeConversions(SILGenFunction &SGF, const Conversion &outerConversion,
299-
const Conversion &innerConversion);
297+
bool canPeepholeConversions(SILGenFunction &SGF,
298+
const Conversion &outer,
299+
const Conversion &inner);
300300

301301
/// An initialization where we ultimately want to apply a conversion to
302302
/// the value before completing the initialization.

lib/SILGen/SILGenConvert.cpp

Lines changed: 166 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
using namespace swift;
3535
using namespace Lowering;
3636

37+
static std::optional<ConversionPeepholeHint>
38+
combineConversions(SILGenFunction &SGF, const Conversion &outer,
39+
const Conversion &inner);
40+
41+
3742
// FIXME: With some changes to their callers, all of the below functions
3843
// could be re-worked to use emitInjectEnum().
3944
ManagedValue
@@ -1255,7 +1260,7 @@ bool ConvertingInitialization::tryPeephole(SILGenFunction &SGF, SILLocation loc,
12551260
Conversion innerConversion,
12561261
ValueProducerRef produceValue) {
12571262
const auto &outerConversion = getConversion();
1258-
auto hint = canPeepholeConversions(SGF, outerConversion, innerConversion);
1263+
auto hint = combineConversions(SGF, outerConversion, innerConversion);
12591264
if (!hint)
12601265
return false;
12611266

@@ -1663,58 +1668,168 @@ static bool isPeepholeableConversion(CanType innerSrcType, CanType innerDestType
16631668
return isPeepholeableConversionImpl(innerSrcType, innerDestType, outerDestType);
16641669
}
16651670

1671+
static std::optional<ConversionPeepholeHint>
1672+
combineReabstract(SILGenFunction &SGF,
1673+
const Conversion &outer,
1674+
const Conversion &inner) {
1675+
// We can never combine conversions in a way that would lose information
1676+
// about the intermediate types.
1677+
if (!isPeepholeableConversion(inner.getReabstractionInputSubstType(),
1678+
inner.getReabstractionOutputSubstType(),
1679+
outer.getReabstractionInputSubstType(),
1680+
outer.getReabstractionOutputSubstType()))
1681+
return std::nullopt;
1682+
1683+
// Recognize when the whole conversion is an identity.
1684+
if (inner.getReabstractionInputLoweredType().getObjectType() ==
1685+
outer.getReabstractionOutputLoweredType().getObjectType())
1686+
return ConversionPeepholeHint(ConversionPeepholeHint::Identity, false);
1687+
1688+
return ConversionPeepholeHint(ConversionPeepholeHint::Reabstract, false);
1689+
}
1690+
1691+
static std::optional<ConversionPeepholeHint>
1692+
combineSubtypeIntoReabstract(SILGenFunction &SGF,
1693+
const Conversion &outer,
1694+
const Conversion &inner) {
1695+
// We can never combine conversions in a way that would lose information
1696+
// about the intermediate types.
1697+
if (!isPeepholeableConversion(inner.getBridgingSourceType(),
1698+
inner.getBridgingResultType(),
1699+
outer.getReabstractionInputSubstType(),
1700+
outer.getReabstractionOutputSubstType()))
1701+
return std::nullopt;
1702+
1703+
return ConversionPeepholeHint(
1704+
ConversionPeepholeHint::SubtypeIntoReabstract, false);
1705+
}
1706+
1707+
static std::optional<ConversionPeepholeHint>
1708+
combineSubtype(SILGenFunction &SGF,
1709+
const Conversion &outer, const Conversion &inner) {
1710+
if (!isPeepholeableConversion(inner.getBridgingSourceType(),
1711+
inner.getBridgingResultType(),
1712+
outer.getBridgingSourceType(),
1713+
outer.getBridgingResultType()))
1714+
return std::nullopt;
1715+
1716+
return ConversionPeepholeHint(ConversionPeepholeHint::Subtype, false);
1717+
}
1718+
1719+
static std::optional<ConversionPeepholeHint>
1720+
combineBridging(SILGenFunction &SGF,
1721+
const Conversion &outer, const Conversion &inner) {
1722+
bool outerExplicit = outer.isBridgingExplicit();
1723+
bool innerExplicit = inner.isBridgingExplicit();
1724+
1725+
// Never peephole if both conversions are explicit; there might be
1726+
// something the user's trying to do which we don't understand.
1727+
if (outerExplicit && innerExplicit)
1728+
return std::nullopt;
1729+
1730+
// Otherwise, we can peephole if we understand the resulting conversion
1731+
// and applying the peephole doesn't change semantics.
1732+
1733+
CanType sourceType = inner.getBridgingSourceType();
1734+
CanType intermediateType = inner.getBridgingResultType();
1735+
assert(intermediateType == outer.getBridgingSourceType());
1736+
1737+
// If we're doing a peephole involving a force, we want to propagate
1738+
// the force to the source value. If it's not in fact optional, that
1739+
// won't work.
1740+
bool forced = outer.getKind() == Conversion::ForceAndBridgeToObjC;
1741+
if (forced) {
1742+
sourceType = sourceType.getOptionalObjectType();
1743+
if (!sourceType)
1744+
return std::nullopt;
1745+
intermediateType = intermediateType.getOptionalObjectType();
1746+
assert(intermediateType);
1747+
}
1748+
1749+
CanType resultType = outer.getBridgingResultType();
1750+
SILType loweredSourceTy = SGF.getLoweredType(sourceType);
1751+
SILType loweredResultTy = outer.getBridgingLoweredResultType();
1752+
1753+
auto applyPeephole = [&](ConversionPeepholeHint::Kind kind) {
1754+
return ConversionPeepholeHint(kind, forced);
1755+
};
1756+
1757+
// Converting to Any doesn't do anything semantically special, so we
1758+
// can apply the peephole unconditionally.
1759+
if (isMatchedAnyToAnyObjectConversion(intermediateType, resultType)) {
1760+
if (loweredSourceTy == loweredResultTy) {
1761+
return applyPeephole(ConversionPeepholeHint::Identity);
1762+
} else if (isValueToAnyConversion(sourceType, intermediateType)) {
1763+
return applyPeephole(ConversionPeepholeHint::BridgeToAnyObject);
1764+
} else {
1765+
return applyPeephole(ConversionPeepholeHint::Subtype);
1766+
}
1767+
}
1768+
1769+
// Otherwise, undoing a bridging conversions can change semantics by
1770+
// e.g. removing a copy, so we shouldn't do it unless the special
1771+
// syntactic bridging peephole applies. That requires one of the
1772+
// conversions to be explicit.
1773+
// TODO: use special SILGen to preserve semantics in this case,
1774+
// e.g. by making a copy.
1775+
if (!outerExplicit && !innerExplicit) {
1776+
return std::nullopt;
1777+
}
1778+
1779+
// Okay, now we're in the domain of the bridging peephole: an
1780+
// explicit bridging conversion can cancel out an implicit bridge
1781+
// between related types.
1782+
1783+
// If the source and destination types have exactly the same
1784+
// representation, then (1) they're related and (2) we can directly
1785+
// emit into the context.
1786+
if (loweredSourceTy.getObjectType() == loweredResultTy.getObjectType()) {
1787+
return applyPeephole(ConversionPeepholeHint::Identity);
1788+
}
1789+
1790+
// Look for a subtype relationship between the source and destination.
1791+
if (areRelatedTypesForBridgingPeephole(sourceType, resultType)) {
1792+
return applyPeephole(ConversionPeepholeHint::Subtype);
1793+
}
1794+
1795+
// If the inner conversion is a result conversion that removes
1796+
// optionality, and the non-optional source type is a subtype of the
1797+
// value type, this is just an implicit force.
1798+
if (!forced &&
1799+
inner.getKind() == Conversion::BridgeResultFromObjC) {
1800+
if (auto sourceValueType = sourceType.getOptionalObjectType()) {
1801+
if (!intermediateType.getOptionalObjectType() &&
1802+
areRelatedTypesForBridgingPeephole(sourceValueType, resultType)) {
1803+
forced = true;
1804+
return applyPeephole(ConversionPeepholeHint::Subtype);
1805+
}
1806+
}
1807+
}
1808+
1809+
return std::nullopt;
1810+
}
1811+
16661812
/// TODO: this would really be a lot cleaner if it just returned a
16671813
/// std::optional<Conversion>.
1668-
std::optional<ConversionPeepholeHint>
1669-
Lowering::canPeepholeConversions(SILGenFunction &SGF,
1670-
const Conversion &outerConversion,
1671-
const Conversion &innerConversion) {
1672-
switch (outerConversion.getKind()) {
1814+
static std::optional<ConversionPeepholeHint>
1815+
combineConversions(SILGenFunction &SGF, const Conversion &outer,
1816+
const Conversion &inner) {
1817+
switch (outer.getKind()) {
16731818
case Conversion::Reabstract:
1674-
switch (innerConversion.getKind()) {
1819+
switch (inner.getKind()) {
16751820
case Conversion::Reabstract:
1676-
// We can never combine conversions in a way that would lose information
1677-
// about the intermediate types.
1678-
if (!isPeepholeableConversion(
1679-
innerConversion.getReabstractionInputSubstType(),
1680-
innerConversion.getReabstractionOutputSubstType(),
1681-
outerConversion.getReabstractionInputSubstType(),
1682-
outerConversion.getReabstractionOutputSubstType()))
1683-
return std::nullopt;
1684-
1685-
// Recognize when the whole conversion is an identity.
1686-
if (innerConversion.getReabstractionInputLoweredType().getObjectType() ==
1687-
outerConversion.getReabstractionOutputLoweredType().getObjectType())
1688-
return ConversionPeepholeHint(ConversionPeepholeHint::Identity, false);
1689-
1690-
return ConversionPeepholeHint(ConversionPeepholeHint::Reabstract, false);
1821+
return combineReabstract(SGF, outer, inner);
16911822

16921823
case Conversion::Subtype:
1693-
// We can never combine conversions in a way that would lose information
1694-
// about the intermediate types.
1695-
if (!isPeepholeableConversion(
1696-
innerConversion.getBridgingSourceType(),
1697-
innerConversion.getBridgingResultType(),
1698-
outerConversion.getReabstractionInputSubstType(),
1699-
outerConversion.getReabstractionOutputSubstType()))
1700-
return std::nullopt;
1701-
1702-
return ConversionPeepholeHint(
1703-
ConversionPeepholeHint::SubtypeIntoReabstract, false);
1824+
return combineSubtypeIntoReabstract(SGF, outer, inner);
17041825

17051826
default:
1706-
break;
1827+
return std::nullopt;
17071828
}
17081829

1709-
return std::nullopt;
1710-
17111830
case Conversion::Subtype:
1712-
if (innerConversion.getKind() == Conversion::Subtype &&
1713-
isPeepholeableConversion(innerConversion.getBridgingSourceType(),
1714-
innerConversion.getBridgingResultType(),
1715-
outerConversion.getBridgingSourceType(),
1716-
outerConversion.getBridgingResultType()))
1717-
return ConversionPeepholeHint(ConversionPeepholeHint::Subtype, false);
1831+
if (inner.getKind() == Conversion::Subtype)
1832+
return combineSubtype(SGF, outer, inner);
17181833
return std::nullopt;
17191834

17201835
case Conversion::AnyErasure:
@@ -1726,104 +1841,21 @@ Lowering::canPeepholeConversions(SILGenFunction &SGF,
17261841

17271842
case Conversion::ForceAndBridgeToObjC:
17281843
case Conversion::BridgeToObjC:
1729-
switch (innerConversion.getKind()) {
1844+
switch (inner.getKind()) {
17301845
case Conversion::AnyErasure:
17311846
case Conversion::BridgeFromObjC:
1732-
case Conversion::BridgeResultFromObjC: {
1733-
bool outerExplicit = outerConversion.isBridgingExplicit();
1734-
bool innerExplicit = innerConversion.isBridgingExplicit();
1735-
1736-
// Never peephole if both conversions are explicit; there might be
1737-
// something the user's trying to do which we don't understand.
1738-
if (outerExplicit && innerExplicit)
1739-
return std::nullopt;
1740-
1741-
// Otherwise, we can peephole if we understand the resulting conversion
1742-
// and applying the peephole doesn't change semantics.
1743-
1744-
CanType sourceType = innerConversion.getBridgingSourceType();
1745-
CanType intermediateType = innerConversion.getBridgingResultType();
1746-
assert(intermediateType == outerConversion.getBridgingSourceType());
1747-
1748-
// If we're doing a peephole involving a force, we want to propagate
1749-
// the force to the source value. If it's not in fact optional, that
1750-
// won't work.
1751-
bool forced =
1752-
outerConversion.getKind() == Conversion::ForceAndBridgeToObjC;
1753-
if (forced) {
1754-
sourceType = sourceType.getOptionalObjectType();
1755-
if (!sourceType)
1756-
return std::nullopt;
1757-
intermediateType = intermediateType.getOptionalObjectType();
1758-
assert(intermediateType);
1759-
}
1760-
1761-
CanType resultType = outerConversion.getBridgingResultType();
1762-
SILType loweredSourceTy = SGF.getLoweredType(sourceType);
1763-
SILType loweredResultTy = outerConversion.getBridgingLoweredResultType();
1764-
1765-
auto applyPeephole = [&](ConversionPeepholeHint::Kind kind) {
1766-
return ConversionPeepholeHint(kind, forced);
1767-
};
1768-
1769-
// Converting to Any doesn't do anything semantically special, so we
1770-
// can apply the peephole unconditionally.
1771-
if (isMatchedAnyToAnyObjectConversion(intermediateType, resultType)) {
1772-
if (loweredSourceTy == loweredResultTy) {
1773-
return applyPeephole(ConversionPeepholeHint::Identity);
1774-
} else if (isValueToAnyConversion(sourceType, intermediateType)) {
1775-
return applyPeephole(ConversionPeepholeHint::BridgeToAnyObject);
1776-
} else {
1777-
return applyPeephole(ConversionPeepholeHint::Subtype);
1778-
}
1779-
}
1780-
1781-
// Otherwise, undoing a bridging conversions can change semantics by
1782-
// e.g. removing a copy, so we shouldn't do it unless the special
1783-
// syntactic bridging peephole applies. That requires one of the
1784-
// conversions to be explicit.
1785-
// TODO: use special SILGen to preserve semantics in this case,
1786-
// e.g. by making a copy.
1787-
if (!outerExplicit && !innerExplicit) {
1788-
return std::nullopt;
1789-
}
1790-
1791-
// Okay, now we're in the domain of the bridging peephole: an
1792-
// explicit bridging conversion can cancel out an implicit bridge
1793-
// between related types.
1794-
1795-
// If the source and destination types have exactly the same
1796-
// representation, then (1) they're related and (2) we can directly
1797-
// emit into the context.
1798-
if (loweredSourceTy.getObjectType() == loweredResultTy.getObjectType()) {
1799-
return applyPeephole(ConversionPeepholeHint::Identity);
1800-
}
1801-
1802-
// Look for a subtype relationship between the source and destination.
1803-
if (areRelatedTypesForBridgingPeephole(sourceType, resultType)) {
1804-
return applyPeephole(ConversionPeepholeHint::Subtype);
1805-
}
1806-
1807-
// If the inner conversion is a result conversion that removes
1808-
// optionality, and the non-optional source type is a subtype of the
1809-
// value type, this is just an implicit force.
1810-
if (!forced &&
1811-
innerConversion.getKind() == Conversion::BridgeResultFromObjC) {
1812-
if (auto sourceValueType = sourceType.getOptionalObjectType()) {
1813-
if (!intermediateType.getOptionalObjectType() &&
1814-
areRelatedTypesForBridgingPeephole(sourceValueType, resultType)) {
1815-
forced = true;
1816-
return applyPeephole(ConversionPeepholeHint::Subtype);
1817-
}
1818-
}
1819-
}
1820-
1821-
return std::nullopt;
1822-
}
1847+
case Conversion::BridgeResultFromObjC:
1848+
return combineBridging(SGF, outer, inner);
18231849

18241850
default:
18251851
return std::nullopt;
18261852
}
18271853
}
18281854
llvm_unreachable("bad kind");
18291855
}
1856+
1857+
bool Lowering::canPeepholeConversions(SILGenFunction &SGF,
1858+
const Conversion &outer,
1859+
const Conversion &inner) {
1860+
return combineConversions(SGF, outer, inner).has_value();
1861+
}

0 commit comments

Comments
 (0)