Skip to content

Commit 0f9e4fc

Browse files
authored
Merge pull request #78599 from atrick/fix-diagnostic-hack
LifetimeDependenceDiagnostics; remove a bootstrapping hack.
2 parents e89abf8 + 8b02bd7 commit 0f9e4fc

11 files changed

+242
-79
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -199,21 +199,6 @@ private struct DiagnoseDependence {
199199
if function.hasUnsafeNonEscapableResult {
200200
return .continueWalk
201201
}
202-
// FIXME: remove this condition once we have a Builtin.dependence,
203-
// which developers should use to model the unsafe
204-
// dependence. Builtin.lifetime_dependence will be lowered to
205-
// mark_dependence [unresolved], which will be checked
206-
// independently. Instead, of this function result check, allow
207-
// isUnsafeApplyResult to be used be mark_dependence [unresolved]
208-
// without checking its dependents.
209-
//
210-
// Allow returning an apply result (@_unsafeNonescapableResult) if
211-
// the calling function has a dependence. This implicitly makes
212-
// the unsafe nonescapable result dependent on the calling
213-
// function's lifetime dependence arguments.
214-
if dependence.isUnsafeApplyResult, function.hasResultDependence {
215-
return .continueWalk
216-
}
217202
// Check that the parameter dependence for this result is the same
218203
// as the current dependence scope.
219204
if let arg = dependence.scope.parentValue as? FunctionArgument,

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,6 @@ extension LifetimeDependence {
225225
self.dependentValue = value
226226
}
227227

228-
var isUnsafeApplyResult: Bool {
229-
if case let .owned(value) = scope {
230-
if let apply = value.definingInstruction as? FullApplySite {
231-
assert(!apply.hasResultDependence)
232-
return true
233-
}
234-
}
235-
return false
236-
}
237-
238228
/// Construct LifetimeDependence from mark_dependence [unresolved] or mark_dependence [nonescaping].
239229
///
240230
/// For any LifetimeDependence constructed from a mark_dependence, its `dependentValue` will be the result of the

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,28 @@ getFullyReferenceableStruct(SILType Ty) {
9191
return SD;
9292
}
9393

94-
static unsigned getNumSubElements(SILType T, SILModule &M,
94+
static unsigned getNumSubElements(SILType T, SILFunction &F,
9595
TypeExpansionContext context) {
9696

9797
if (auto TT = T.getAs<TupleType>()) {
9898
unsigned NumElements = 0;
9999
for (auto index : indices(TT.getElementTypes()))
100100
NumElements +=
101-
getNumSubElements(T.getTupleElementType(index), M, context);
101+
getNumSubElements(T.getTupleElementType(index), F, context);
102102
return NumElements;
103103
}
104104

105105
if (auto *SD = getFullyReferenceableStruct(T)) {
106106
unsigned NumElements = 0;
107107
for (auto *D : SD->getStoredProperties())
108108
NumElements +=
109-
getNumSubElements(T.getFieldType(D, M, context), M, context);
110-
return NumElements;
109+
getNumSubElements(T.getFieldType(D, F.getModule(), context), F,
110+
context);
111+
112+
// Returning NumElements == 0 implies that "empty" values can be
113+
// materialized without ownership. This is only valid for trivial types.
114+
if (NumElements > 0 || T.isTrivial(F))
115+
return NumElements;
111116
}
112117

113118
// If this isn't a tuple or struct, it is a single element.
@@ -152,7 +157,7 @@ static SILValue getAccessPathRoot(SILValue pointer) {
152157
static unsigned computeSubelement(SILValue Pointer,
153158
SingleValueInstruction *RootInst) {
154159
unsigned SubElementNumber = 0;
155-
SILModule &M = RootInst->getModule();
160+
auto &F = *RootInst->getFunction();
156161

157162
while (1) {
158163
// If we got to the root, we're done.
@@ -175,7 +180,7 @@ static unsigned computeSubelement(SILValue Pointer,
175180
// Keep track of what subelement is being referenced.
176181
for (unsigned i = 0, e = TEAI->getFieldIndex(); i != e; ++i) {
177182
SubElementNumber +=
178-
getNumSubElements(TT.getTupleElementType(i), M,
183+
getNumSubElements(TT.getTupleElementType(i), F,
179184
TypeExpansionContext(*RootInst->getFunction()));
180185
}
181186
Pointer = TEAI->getOperand();
@@ -191,7 +196,8 @@ static unsigned computeSubelement(SILValue Pointer,
191196
if (D == SEAI->getField()) break;
192197
auto context = TypeExpansionContext(*RootInst->getFunction());
193198
SubElementNumber +=
194-
getNumSubElements(ST.getFieldType(D, M, context), M, context);
199+
getNumSubElements(ST.getFieldType(D, F.getModule(), context), F,
200+
context);
195201
}
196202

197203
Pointer = SEAI->getOperand();
@@ -370,10 +376,9 @@ static bool isFullyAvailable(SILType loadTy, unsigned firstElt,
370376
if (!firstVal || firstVal.getType() != loadTy)
371377
return false;
372378

373-
auto *function = firstVal.getValue()->getFunction();
379+
auto &function = *firstVal.getValue()->getFunction();
374380
return llvm::all_of(
375-
range(getNumSubElements(loadTy, function->getModule(),
376-
TypeExpansionContext(*function))),
381+
range(getNumSubElements(loadTy, function, TypeExpansionContext(function))),
377382
[&](unsigned index) -> bool {
378383
auto &val = AvailableValues[firstElt + index];
379384
return val.getValue() == firstVal.getValue() &&
@@ -395,7 +400,7 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val,
395400
// Keep track of what subelement is being referenced.
396401
SILType EltTy = ValTy.getTupleElementType(EltNo);
397402
unsigned NumSubElt = getNumSubElements(
398-
EltTy, B.getModule(), TypeExpansionContext(B.getFunction()));
403+
EltTy, B.getFunction(), TypeExpansionContext(B.getFunction()));
399404
if (SubElementNumber < NumSubElt) {
400405
auto BorrowedVal = Val.emitBeginBorrow(B, Loc);
401406
auto NewVal =
@@ -420,7 +425,7 @@ static SILValue nonDestructivelyExtractSubElement(const AvailableValue &Val,
420425
auto fieldType = ValTy.getFieldType(
421426
D, B.getModule(), TypeExpansionContext(B.getFunction()));
422427
unsigned NumSubElt = getNumSubElements(
423-
fieldType, B.getModule(), TypeExpansionContext(B.getFunction()));
428+
fieldType, B.getFunction(), TypeExpansionContext(B.getFunction()));
424429

425430
if (SubElementNumber < NumSubElt) {
426431
auto BorrowedVal = Val.emitBeginBorrow(B, Loc);
@@ -1227,7 +1232,8 @@ bool AvailableValueAggregator::canTake(SILType loadTy,
12271232
return llvm::all_of(indices(tt->getElements()), [&](unsigned eltNo) {
12281233
SILType eltTy = loadTy.getTupleElementType(eltNo);
12291234
unsigned numSubElt =
1230-
getNumSubElements(eltTy, M, TypeExpansionContext(B.getFunction()));
1235+
getNumSubElements(eltTy, B.getFunction(),
1236+
TypeExpansionContext(B.getFunction()));
12311237
bool success = canTake(eltTy, firstElt);
12321238
firstElt += numSubElt;
12331239
return success;
@@ -1238,7 +1244,7 @@ bool AvailableValueAggregator::canTake(SILType loadTy,
12381244
return llvm::all_of(sd->getStoredProperties(), [&](VarDecl *decl) -> bool {
12391245
auto context = TypeExpansionContext(B.getFunction());
12401246
SILType eltTy = loadTy.getFieldType(decl, M, context);
1241-
unsigned numSubElt = getNumSubElements(eltTy, M, context);
1247+
unsigned numSubElt = getNumSubElements(eltTy, B.getFunction(), context);
12421248
bool success = canTake(eltTy, firstElt);
12431249
firstElt += numSubElt;
12441250
return success;
@@ -1369,7 +1375,8 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT,
13691375
for (unsigned EltNo : indices(TT->getElements())) {
13701376
SILType EltTy = LoadTy.getTupleElementType(EltNo);
13711377
unsigned NumSubElt =
1372-
getNumSubElements(EltTy, M, TypeExpansionContext(B.getFunction()));
1378+
getNumSubElements(EltTy, B.getFunction(),
1379+
TypeExpansionContext(B.getFunction()));
13731380

13741381
// If we are missing any of the available values in this struct element,
13751382
// compute an address to load from.
@@ -1406,7 +1413,7 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd,
14061413
for (auto *decl : sd->getStoredProperties()) {
14071414
auto context = TypeExpansionContext(B.getFunction());
14081415
SILType eltTy = loadTy.getFieldType(decl, M, context);
1409-
unsigned numSubElt = getNumSubElements(eltTy, M, context);
1416+
unsigned numSubElt = getNumSubElements(eltTy, B.getFunction(), context);
14101417

14111418
// If we are missing any of the available values in this struct element,
14121419
// compute an address to load from.
@@ -1540,6 +1547,8 @@ class AvailableValueDataflowContext {
15401547
private:
15411548
SILModule &getModule() const { return TheMemory->getModule(); }
15421549

1550+
SILFunction &getFunction() const { return *TheMemory->getFunction(); }
1551+
15431552
void updateAvailableValues(
15441553
SILInstruction *Inst,
15451554
SmallBitVector &RequiredElts,
@@ -1673,7 +1682,7 @@ AvailableValueDataflowContext::computeAvailableValues(
16731682
return std::nullopt;
16741683

16751684
unsigned NumLoadSubElements = getNumSubElements(
1676-
LoadTy, getModule(), TypeExpansionContext(*TheMemory->getFunction()));
1685+
LoadTy, getFunction(), TypeExpansionContext(getFunction()));
16771686

16781687
LoadInfo loadInfo = {LoadTy, FirstElt, NumLoadSubElements};
16791688

@@ -1712,14 +1721,14 @@ static inline void updateAvailableValuesHelper(
17121721
SmallBitVector &conflictingValues,
17131722
function_ref<std::optional<AvailableValue>(unsigned)> defaultFunc,
17141723
function_ref<bool(AvailableValue &, unsigned)> isSafeFunc) {
1715-
auto &mod = theMemory->getModule();
17161724
unsigned startSubElt = computeSubelement(address, theMemory);
17171725

17181726
// TODO: Is this needed now?
17191727
assert(startSubElt != ~0U && "Store within enum projection not handled");
1728+
auto &f = *theMemory->getFunction();
17201729
for (unsigned i : range(getNumSubElements(
1721-
address->getType().getObjectType(), mod,
1722-
TypeExpansionContext(*theMemory->getFunction())))) {
1730+
address->getType().getObjectType(), f,
1731+
TypeExpansionContext(f)))) {
17231732
// If this element is not required, don't fill it in.
17241733
if (!requiredElts[startSubElt + i])
17251734
continue;
@@ -1849,7 +1858,8 @@ void AvailableValueDataflowContext::updateAvailableValues(
18491858

18501859
bool AnyRequired = false;
18511860
for (unsigned i : range(getNumSubElements(
1852-
ValTy, getModule(), TypeExpansionContext(*CAI->getFunction())))) {
1861+
ValTy, getFunction(),
1862+
TypeExpansionContext(getFunction())))) {
18531863
// If this element is not required, don't fill it in.
18541864
AnyRequired = RequiredElts[StartSubElt+i];
18551865
if (AnyRequired) break;
@@ -1883,7 +1893,7 @@ void AvailableValueDataflowContext::updateAvailableValues(
18831893
// potentially bailing out (because it is address-only).
18841894
bool AnyRequired = false;
18851895
for (unsigned i : range(getNumSubElements(
1886-
ValTy, getModule(), TypeExpansionContext(*MD->getFunction())))) {
1896+
ValTy, getFunction(), TypeExpansionContext(getFunction())))) {
18871897
// If this element is not required, don't fill it in.
18881898
AnyRequired = RequiredElts[StartSubElt+i];
18891899
if (AnyRequired) break;
@@ -2171,7 +2181,7 @@ void AvailableValueDataflowContext::updateMarkDependenceValues(
21712181
}
21722182
SILType valueTy = md->getValue()->getType().getObjectType();
21732183
unsigned numMDSubElements = getNumSubElements(
2174-
valueTy, getModule(), TypeExpansionContext(*TheMemory->getFunction()));
2184+
valueTy, getFunction(), TypeExpansionContext(getFunction()));
21752185

21762186
// Update each required subelement of the mark_dependence value.
21772187
for (unsigned subIdx = firstMDElt; subIdx < firstMDElt + numMDSubElements;
@@ -2299,7 +2309,8 @@ class OptimizeAllocLoads {
22992309
: Module(memory->getModule()), TheMemory(memory),
23002310
MemoryType(getMemoryType(memory)),
23012311
NumMemorySubElements(getNumSubElements(
2302-
MemoryType, Module, TypeExpansionContext(*memory->getFunction()))),
2312+
MemoryType, *memory->getFunction(),
2313+
TypeExpansionContext(*memory->getFunction()))),
23032314
Uses(uses), deleter(deleter), deadEndBlocks(deadEndBlocks),
23042315
DataflowContext(TheMemory, NumMemorySubElements,
23052316
OptimizationMode::PreserveAlloc, uses,
@@ -2680,7 +2691,8 @@ class OptimizeDeadAlloc {
26802691
: Module(memory->getModule()), TheMemory(memory),
26812692
MemoryType(getMemoryType(memory)),
26822693
NumMemorySubElements(getNumSubElements(
2683-
MemoryType, Module, TypeExpansionContext(*memory->getFunction()))),
2694+
MemoryType, *memory->getFunction(),
2695+
TypeExpansionContext(*memory->getFunction()))),
26842696
Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks),
26852697
deleter(deleter), domInfo(domInfo),
26862698
DataflowContext(TheMemory, NumMemorySubElements,

test/SIL/explicit_lifetime_dependence_specifiers.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,30 @@
88

99
import Builtin
1010

11+
@_unsafeNonescapableResult
12+
@lifetime(borrow source)
13+
internal func _overrideLifetime<
14+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
15+
>(
16+
_ dependent: consuming T, borrowing source: borrowing U
17+
) -> T {
18+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
19+
// should be expressed by a builtin that is hidden within the function body.
20+
dependent
21+
}
22+
23+
@_unsafeNonescapableResult
24+
@lifetime(source)
25+
internal func _overrideLifetime<
26+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
27+
>(
28+
_ dependent: consuming T, copying source: borrowing U
29+
) -> T {
30+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
31+
// should be expressed by a builtin that is hidden within the function body.
32+
dependent
33+
}
34+
1135
struct BufferView : ~Escapable {
1236
let ptr: UnsafeRawBufferPointer
1337
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers10BufferViewVyACSWcfC : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @lifetime(borrow 0) @owned BufferView {
@@ -22,7 +46,7 @@ struct BufferView : ~Escapable {
2246
}
2347
self.ptr = ptr
2448
}
25-
@_unsafeNonescapableResult
49+
@lifetime(borrow ptr)
2650
init(independent ptr: UnsafeRawBufferPointer) {
2751
self.ptr = ptr
2852
}
@@ -71,7 +95,8 @@ func derive(_ x: borrowing BufferView) -> BufferView {
7195
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16consumeAndCreateyAA10BufferViewVADnF : $@convention(thin) (@owned BufferView) -> @lifetime(copy 0) @owned BufferView {
7296
@lifetime(x)
7397
func consumeAndCreate(_ x: consuming BufferView) -> BufferView {
74-
return BufferView(independent: x.ptr)
98+
let bv = BufferView(independent: x.ptr)
99+
return _overrideLifetime(bv, copying: x)
75100
}
76101

77102
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers17deriveThisOrThat1yAA10BufferViewVAD_ADtF : $@convention(thin) (@guaranteed BufferView, @guaranteed BufferView) -> @lifetime(copy 1, borrow 0) @owned BufferView {
@@ -89,7 +114,8 @@ func deriveThisOrThat2(_ this: borrowing BufferView, _ that: consuming BufferVie
89114
if (Int.random(in: 1..<100) == 0) {
90115
return BufferView(independent: this.ptr)
91116
}
92-
return BufferView(independent: that.ptr)
117+
let bv = BufferView(independent: that.ptr)
118+
return _overrideLifetime(bv, copying: that)
93119
}
94120

95121
func use(_ x: borrowing BufferView) {}
@@ -123,12 +149,12 @@ struct Container : ~Escapable {
123149
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16getConsumingViewyAA06BufferG0VAA9ContainerVnF : $@convention(thin) (@owned Container) -> @lifetime(copy 0) @owned BufferView {
124150
@lifetime(x)
125151
func getConsumingView(_ x: consuming Container) -> BufferView {
126-
return BufferView(independent: x.ptr)
152+
let bv = BufferView(independent: x.ptr)
153+
return _overrideLifetime(bv, copying: x)
127154
}
128155

129156
// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16getBorrowingViewyAA06BufferG0VAA9ContainerVF : $@convention(thin) (@guaranteed Container) -> @lifetime(borrow 0) @owned BufferView {
130157
@lifetime(borrow x)
131158
func getBorrowingView(_ x: borrowing Container) -> BufferView {
132159
return BufferView(independent: x.ptr)
133160
}
134-

test/SIL/implicit_lifetime_dependence.swift

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,27 @@
55

66
// REQUIRES: swift_feature_LifetimeDependence
77

8+
@_unsafeNonescapableResult
9+
@lifetime(borrow source)
10+
internal func _overrideLifetime<
11+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
12+
>(
13+
_ dependent: consuming T, borrowing source: borrowing U
14+
) -> T {
15+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
16+
// should be expressed by a builtin that is hidden within the function body.
17+
dependent
18+
}
19+
820
@_unsafeNonescapableResult
921
@lifetime(source)
10-
func unsafeLifetime<T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable>(
11-
dependent: consuming T, dependsOn source: borrowing U)
12-
-> T {
22+
internal func _overrideLifetime<
23+
T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable
24+
>(
25+
_ dependent: consuming T, copying source: borrowing U
26+
) -> T {
27+
// TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence
28+
// should be expressed by a builtin that is hidden within the function body.
1329
dependent
1430
}
1531

@@ -21,7 +37,7 @@ struct BufferView : ~Escapable {
2137
self.ptr = ptr
2238
self.c = c
2339
}
24-
@_unsafeNonescapableResult
40+
@lifetime(borrow ptr)
2541
init(independent ptr: UnsafeRawBufferPointer, _ c: Int) {
2642
self.ptr = ptr
2743
self.c = c
@@ -74,7 +90,8 @@ func derive(_ unused: Int, _ x: borrowing BufferView) -> BufferView {
7490

7591
// CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence16consumeAndCreateyAA10BufferViewVADnF : $@convention(thin) (@owned BufferView) -> @lifetime(copy 0) @owned BufferView {
7692
func consumeAndCreate(_ x: consuming BufferView) -> BufferView {
77-
return BufferView(independent: x.ptr, x.c)
93+
let bv = BufferView(independent: x.ptr, x.c)
94+
return _overrideLifetime(bv, copying: x)
7895
}
7996

8097
func use(_ x: borrowing BufferView) {}
@@ -175,7 +192,7 @@ struct GenericBufferView<Element> : ~Escapable {
175192
count: bounds.upperBound.distance(to:bounds.lowerBound) / MemoryLayout<Element>.stride
176193
)
177194
// assuming that bounds is within self
178-
return unsafeLifetime(dependent: result, dependsOn: self)
195+
return _overrideLifetime(result, copying: self)
179196
}
180197
}
181198
}

0 commit comments

Comments
 (0)