Skip to content

Commit 1609384

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

File tree

2 files changed

+169
-137
lines changed

2 files changed

+169
-137
lines changed

lib/SILGen/Conversion.h

+3-3
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

+166-134
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
@@ -1247,7 +1252,7 @@ bool ConvertingInitialization::tryPeephole(SILGenFunction &SGF, SILLocation loc,
12471252
Conversion innerConversion,
12481253
ValueProducerRef produceValue) {
12491254
const auto &outerConversion = getConversion();
1250-
auto hint = canPeepholeConversions(SGF, outerConversion, innerConversion);
1255+
auto hint = combineConversions(SGF, outerConversion, innerConversion);
12511256
if (!hint)
12521257
return false;
12531258

@@ -1655,58 +1660,168 @@ static bool isPeepholeableConversion(CanType innerSrcType, CanType innerDestType
16551660
return isPeepholeableConversionImpl(innerSrcType, innerDestType, outerDestType);
16561661
}
16571662

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

16841815
case Conversion::Subtype:
1685-
// We can never combine conversions in a way that would lose information
1686-
// about the intermediate types.
1687-
if (!isPeepholeableConversion(
1688-
innerConversion.getBridgingSourceType(),
1689-
innerConversion.getBridgingResultType(),
1690-
outerConversion.getReabstractionInputSubstType(),
1691-
outerConversion.getReabstractionOutputSubstType()))
1692-
return std::nullopt;
1693-
1694-
return ConversionPeepholeHint(
1695-
ConversionPeepholeHint::SubtypeIntoReabstract, false);
1816+
return combineSubtypeIntoReabstract(SGF, outer, inner);
16961817

16971818
default:
1698-
break;
1819+
return std::nullopt;
16991820
}
17001821

1701-
return std::nullopt;
1702-
17031822
case Conversion::Subtype:
1704-
if (innerConversion.getKind() == Conversion::Subtype &&
1705-
isPeepholeableConversion(innerConversion.getBridgingSourceType(),
1706-
innerConversion.getBridgingResultType(),
1707-
outerConversion.getBridgingSourceType(),
1708-
outerConversion.getBridgingResultType()))
1709-
return ConversionPeepholeHint(ConversionPeepholeHint::Subtype, false);
1823+
if (inner.getKind() == Conversion::Subtype)
1824+
return combineSubtype(SGF, outer, inner);
17101825
return std::nullopt;
17111826

17121827
case Conversion::AnyErasure:
@@ -1718,104 +1833,21 @@ Lowering::canPeepholeConversions(SILGenFunction &SGF,
17181833

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

18161842
default:
18171843
return std::nullopt;
18181844
}
18191845
}
18201846
llvm_unreachable("bad kind");
18211847
}
1848+
1849+
bool Lowering::canPeepholeConversions(SILGenFunction &SGF,
1850+
const Conversion &outer,
1851+
const Conversion &inner) {
1852+
return combineConversions(SGF, outer, inner).has_value();
1853+
}

0 commit comments

Comments
 (0)