Skip to content

Commit 80b180a

Browse files
committed
Implement a syntactic peephole to recognize explicit bridging
conversions that reverse an implicit conversion done to align foreign declarations with their imported types. For example, consider an Objective-C method that returns an NSString*: - (nonnull NSString*) foo; This will be imported into Swift as a method returning a String: func foo() -> String A call to this method will implicitly convert the result to String behind the scenes. If the user then casts the result back to NSString*, that would normally be compiled as an additional conversion. The compiler cannot simply eliminate the conversion because that is not necessarily semantically equivalent. This peephole recognizes as-casts that immediately reverse a bridging conversion as a special case and gives them special power to eliminate both conversions. For example, 'foo() as NSString' will simply return the original return value. In addition to call results, this also applies to call arguments, property accesses, and subscript accesses.
1 parent c005618 commit 80b180a

29 files changed

+1143
-234
lines changed

lib/SILGen/Conversion.h

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ namespace Lowering {
2929
class Conversion {
3030
public:
3131
enum KindTy {
32-
/// An implicit bridging conversion to a foreign type.
32+
/// A bridging conversion to a foreign type.
3333
BridgeToObjC,
3434

35-
/// An implicit bridging conversion from a foreign type.
35+
/// A bridging conversion to a foreign type following a force.
36+
ForceAndBridgeToObjC,
37+
38+
/// A bridging conversion from a foreign type.
3639
BridgeFromObjC,
3740

38-
/// An implicit bridging conversion for a function result.
41+
/// A bridging conversion for a function result.
3942
BridgeResultFromObjC,
4043

4144
/// An erasure to Any (possibly wrapped in optional conversions).
@@ -73,6 +76,7 @@ class Conversion {
7376
static int getStorageIndexForKind(KindTy kind) {
7477
switch (kind) {
7578
case BridgeToObjC:
79+
case ForceAndBridgeToObjC:
7680
case BridgeFromObjC:
7781
case BridgeResultFromObjC:
7882
case AnyErasure:
@@ -155,21 +159,63 @@ class Conversion {
155159
ManagedValue emit(SILGenFunction &SGF, SILLocation loc,
156160
ManagedValue source, SGFContext ctxt) const;
157161

158-
Optional<Conversion> tryPeepholeOptionalInjection() const;
162+
/// Try to form a conversion that does an optional injection
163+
/// or optional-to-optional conversion followed by this conversion.
164+
Optional<Conversion>
165+
adjustForInitialOptionalConversions(CanType newSourceType) const;
166+
167+
/// Try to form a conversion that does a force-value followed by
168+
/// this conversion.
169+
Optional<Conversion> adjustForInitialForceValue() const;
159170

160171
void dump() const LLVM_ATTRIBUTE_USED;
161172
void print(llvm::raw_ostream &out) const;
162173
};
163174

164-
bool canPeepholeConversions(SILGenFunction &SGF,
165-
const Conversion &outerConversion,
166-
const Conversion &innerConversion);
175+
/// Information about how to peephole two conversions.
176+
///
177+
/// This is really the private state of SILGenConvert.
178+
class ConversionPeepholeHint {
179+
public:
180+
enum Kind : uint8_t {
181+
/// The value will be exactly the right type.
182+
Identity,
183+
184+
/// The value needs to be bridged to AnyObject (possibly optionally).
185+
BridgeToAnyObject,
186+
187+
/// The value just needs to undergo a subtype conversion.
188+
Subtype
189+
};
190+
191+
private:
192+
Kind TheKind;
193+
bool Forced;
194+
195+
public:
196+
ConversionPeepholeHint(Kind kind, bool forced)
197+
: TheKind(kind), Forced(forced) {
198+
}
199+
200+
Kind getKind() const { return TheKind; }
201+
202+
/// Does the value need to be forced before the conversion?
203+
/// This comes up with result conversions where the result was imported
204+
/// as non-optional, as well as with implicitly unwrapped optionals.
205+
bool isForced() const { return Forced; }
206+
};
207+
208+
Optional<ConversionPeepholeHint>
209+
canPeepholeConversions(SILGenFunction &SGF,
210+
const Conversion &outerConversion,
211+
const Conversion &innerConversion);
167212

168213
ManagedValue emitPeepholedConversions(SILGenFunction &SGF, SILLocation loc,
169214
const Conversion &outerConversion,
170215
const Conversion &innerConversion,
216+
ConversionPeepholeHint hint,
171217
SGFContext C,
172-
llvm::function_ref<ManagedValue(SGFContext)> produceValue);
218+
ValueProducerRef produceValue);
173219

174220
/// An initialization where we ultimately want to apply a conversion to
175221
/// the value before completing the initialization.
@@ -234,8 +280,7 @@ class ConvertingInitialization final : public Initialization {
234280
/// initialization. The initialization will not yet be finished.
235281
bool tryPeephole(SILGenFunction &SGF, Expr *E, Conversion innerConversion);
236282
bool tryPeephole(SILGenFunction &SGF, SILLocation loc,
237-
Conversion innerConversion,
238-
llvm::function_ref<ManagedValue(SGFContext)> generate);
283+
Conversion innerConversion, ValueProducerRef producer);
239284
bool tryPeephole(SILGenFunction &SGF, SILLocation loc, ManagedValue value,
240285
Conversion innerConversion);
241286

@@ -247,6 +292,13 @@ class ConvertingInitialization final : public Initialization {
247292
State = Initialized;
248293
}
249294

295+
/// Given that an emitter was able to adjust the conversion when
296+
/// emitting into this initialization, continue emission into the
297+
/// new conversion.
298+
ManagedValue emitWithAdjustedConversion(SILGenFunction &SGF, SILLocation loc,
299+
Conversion adjustedConversion,
300+
ValueProducerRef producer);
301+
250302
/// Given the unconverted result, i.e. the result of emitting a
251303
/// value formally of the unconverted type with this initialization
252304
/// as the SGFContext, produce the converted result.

lib/SILGen/LValue.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
namespace swift {
2929
namespace Lowering {
3030

31+
class ArgumentSource;
3132
class LogicalPathComponent;
3233
class ManagedValue;
3334
class PhysicalPathComponent;
@@ -154,6 +155,12 @@ class PathComponent {
154155
TranslationPathComponent &asTranslation();
155156
const TranslationPathComponent &asTranslation() const;
156157

158+
/// Is this some form of open-existential component?
159+
bool isOpenExistential() const {
160+
return getKind() == OpenOpaqueExistentialKind ||
161+
getKind() == OpenNonOpaqueExistentialKind;
162+
}
163+
157164
/// Return the appropriate access kind to use when producing the
158165
/// base value.
159166
virtual AccessKind getBaseAccessKind(SILGenFunction &SGF,
@@ -234,7 +241,7 @@ class LogicalPathComponent : public PathComponent {
234241
///
235242
/// \param base - always an address, but possibly an r-value
236243
virtual void set(SILGenFunction &SGF, SILLocation loc,
237-
RValue &&value, ManagedValue base) && = 0;
244+
ArgumentSource &&value, ManagedValue base) && = 0;
238245

239246
/// Get the property.
240247
///
@@ -304,7 +311,7 @@ class TranslationPathComponent : public LogicalPathComponent {
304311
ManagedValue base, SGFContext c) && override;
305312

306313
void set(SILGenFunction &SGF, SILLocation loc,
307-
RValue &&value, ManagedValue base) && override;
314+
ArgumentSource &&value, ManagedValue base) && override;
308315

309316
/// Transform from the original pattern.
310317
virtual RValue translate(SILGenFunction &SGF, SILLocation loc,
@@ -385,6 +392,13 @@ class LValue {
385392
assert(isLastComponentTranslation());
386393
Path.pop_back();
387394
}
395+
396+
/// Assert that the given component is the last component in the
397+
/// l-value, drop it.
398+
void dropLastComponent(PathComponent &component) & {
399+
assert(&component == Path.back().get());
400+
Path.pop_back();
401+
}
388402

389403
/// Add a new component at the end of the access path of this lvalue.
390404
template <class T, class... As>

lib/SILGen/ManagedValue.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ void ManagedValue::assignInto(SILGenFunction &SGF, SILLocation loc,
126126
IsNotInitialization);
127127
}
128128

129+
void ManagedValue::forwardInto(SILGenFunction &SGF, SILLocation loc,
130+
Initialization *dest) {
131+
dest->copyOrInitValueInto(SGF, loc, *this, /*isInit*/ true);
132+
dest->finishInitialization(SGF);
133+
}
134+
129135
ManagedValue ManagedValue::borrow(SILGenFunction &SGF, SILLocation loc) const {
130136
assert(getValue() && "cannot borrow an invalid or in-context value");
131137
if (isLValue())

lib/SILGen/ManagedValue.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum class CastConsumptionKind : unsigned char;
3131

3232
namespace Lowering {
3333

34+
class Initialization;
3435
class SILGenFunction;
3536

3637
/// ManagedValue - represents a singular SIL value and an optional cleanup.
@@ -287,6 +288,13 @@ class ManagedValue {
287288
/// \param loc - the AST location to associate with emitted instructions.
288289
/// \param address - the address to assign to.
289290
void forwardInto(SILGenFunction &SGF, SILLocation loc, SILValue address);
291+
292+
/// Forward this value into the given initialization.
293+
///
294+
/// \param SGF - The SILGenFunction.
295+
/// \param loc - the AST location to associate with emitted instructions.
296+
/// \param dest - the destination to forward into
297+
void forwardInto(SILGenFunction &SGF, SILLocation loc, Initialization *dest);
290298

291299
/// Assign this value into memory, destroying the existing
292300
/// value at the destination address.

lib/SILGen/RValue.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,35 @@ class RValue {
227227

228228
/// Rewrite the type of this r-value.
229229
void rewriteType(CanType newType) & {
230+
#ifndef NDEBUG
231+
static const auto areSimilarTypes = [](CanType l, CanType r) {
232+
if (l == r) return true;
233+
234+
// Allow function types to disagree about 'noescape'.
235+
if (auto lf = dyn_cast<FunctionType>(l)) {
236+
if (auto rf = dyn_cast<FunctionType>(r)) {
237+
return lf.getInput() == rf.getInput()
238+
&& lf.getResult() == rf.getResult()
239+
&& lf->getExtInfo().withNoEscape(false) ==
240+
lf->getExtInfo().withNoEscape(false);
241+
}
242+
}
243+
return false;
244+
};
245+
246+
static const auto isSingleElementTuple = [](CanType type, CanType eltType) {
247+
if (auto tupleType = dyn_cast<TupleType>(type)) {
248+
return tupleType->getNumElements() == 1 &&
249+
areSimilarTypes(tupleType.getElementType(0), eltType);
250+
}
251+
return false;
252+
};
253+
230254
// We only allow a very modest set of changes to a type.
231-
assert(newType == type ||
232-
(isa<TupleType>(newType) &&
233-
cast<TupleType>(newType)->getNumElements() == 1 &&
234-
cast<TupleType>(newType).getElementType(0) == type));
255+
assert(areSimilarTypes(newType, type) ||
256+
isSingleElementTuple(newType, type) ||
257+
isSingleElementTuple(type, newType));
258+
#endif
235259
type = newType;
236260
}
237261

lib/SILGen/SGFContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ class SGFContext {
158158
}
159159
};
160160

161+
using ValueProducerRef =
162+
llvm::function_ref<ManagedValue(SILGenFunction &gen, SILLocation loc,
163+
SGFContext context)>;
164+
161165
} // end namespace Lowering
162166
} // end namespace swift
163167

lib/SILGen/SILGenApply.cpp

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5164,7 +5164,8 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set,
51645164
SubstitutionList substitutions,
51655165
ArgumentSource &&selfValue,
51665166
bool isSuper, bool isDirectUse,
5167-
RValue &&subscripts, RValue &&setValue) {
5167+
RValue &&subscripts,
5168+
ArgumentSource &&setValue) {
51685169
// Scope any further writeback just within this operation.
51695170
FormalEvaluationScope writebackScope(*this);
51705171

@@ -5184,16 +5185,37 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set,
51845185
// (value) or (value, indices)
51855186
if (!subscripts.isNull()) {
51865187
// If we have a value and index list, create a new rvalue to represent the
5187-
// both of them together. The value goes first.
5188-
SmallVector<ManagedValue, 4> Elts;
5189-
std::move(setValue).getAll(Elts);
5190-
std::move(subscripts).getAll(Elts);
5191-
setValue = RValue::withPreExplodedElements(Elts, accessType.getInput());
5188+
// both of them together.
5189+
auto inputTupleType = cast<TupleType>(accessType.getInput());
5190+
5191+
SmallVector<ArgumentSource, 4> eltSources;
5192+
5193+
// The value comes first.
5194+
eltSources.push_back(std::move(setValue));
5195+
5196+
// The indices come after. Whether they are expanded or not depends on
5197+
// whether they were written as separate parameters, which should be
5198+
// reflected in the params list.
5199+
// TODO: we should really take an array of RValues.
5200+
auto params = accessType.getParams();
5201+
if (params.size() != 2) {
5202+
auto subscriptsTupleType = cast<TupleType>(subscripts.getType());
5203+
assert(inputTupleType->getNumElements()
5204+
== 1 + subscriptsTupleType->getNumElements());
5205+
SmallVector<RValue, 8> eltRVs;
5206+
std::move(subscripts).extractElements(eltRVs);
5207+
for (auto &elt : eltRVs)
5208+
eltSources.emplace_back(loc, std::move(elt));
5209+
} else {
5210+
subscripts.rewriteType(params[1].getType());
5211+
eltSources.emplace_back(loc, std::move(subscripts));
5212+
}
5213+
5214+
setValue = ArgumentSource(loc, inputTupleType, eltSources);
51925215
} else {
51935216
setValue.rewriteType(accessType.getInput());
51945217
}
5195-
emission.addCallSite(loc, ArgumentSource(loc, std::move(setValue)),
5196-
accessType);
5218+
emission.addCallSite(loc, std::move(setValue), accessType);
51975219
// ()
51985220
emission.apply();
51995221
}

0 commit comments

Comments
 (0)