Skip to content

Commit c949e62

Browse files
committed
SIL: Lower fields that are conditionally addressable because of a dependency.
Parameters of generic type need to be treated as potentially addressable-for-dependencies, but we don't want callers using the generic function with concrete types that are known not to be addressable-for- dependencies to be overconstrained. In SILFunctionType lowering, lower these dependencies distinctly as conditionally addressable, meaning that the dependency on an argument depends on whether the concrete type of that argument is (potentially) addressable-for-dependencies or not.
1 parent da81345 commit c949e62

File tree

6 files changed

+101
-27
lines changed

6 files changed

+101
-27
lines changed

include/swift/AST/LifetimeDependence.h

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,25 @@ enum class ParsedLifetimeDependenceKind : uint8_t {
4545
enum class LifetimeDependenceKind : uint8_t { Inherit = 0, Scope };
4646

4747
struct LifetimeDescriptor {
48+
enum IsAddressable_t {
49+
IsNotAddressable,
50+
IsConditionallyAddressable,
51+
IsAddressable,
52+
};
53+
4854
union Value {
4955
struct {
5056
Identifier name;
5157
} Named;
5258
struct {
5359
unsigned index;
54-
bool isAddress;
60+
IsAddressable_t isAddress;
5561
} Ordered;
5662
struct {
5763
} Self;
5864
Value(Identifier name) : Named({name}) {}
59-
Value(unsigned index, bool isAddress) : Ordered({index, isAddress}) {}
65+
Value(unsigned index, IsAddressable_t isAddress)
66+
: Ordered({index, isAddress}) {}
6067
Value() : Self() {}
6168
} value;
6269

@@ -72,7 +79,7 @@ struct LifetimeDescriptor {
7279
SourceLoc loc)
7380
: value{name}, kind(DescriptorKind::Named),
7481
parsedLifetimeDependenceKind(parsedLifetimeDependenceKind), loc(loc) {}
75-
LifetimeDescriptor(unsigned index, bool isAddress,
82+
LifetimeDescriptor(unsigned index, IsAddressable_t isAddress,
7683
ParsedLifetimeDependenceKind parsedLifetimeDependenceKind,
7784
SourceLoc loc)
7885
: value{index, isAddress}, kind(DescriptorKind::Ordered),
@@ -93,7 +100,7 @@ struct LifetimeDescriptor {
93100
forOrdered(unsigned index,
94101
ParsedLifetimeDependenceKind parsedLifetimeDependenceKind,
95102
SourceLoc loc,
96-
bool isAddress = false) {
103+
IsAddressable_t isAddress = IsNotAddressable) {
97104
return {index, isAddress, parsedLifetimeDependenceKind, loc};
98105
}
99106
static LifetimeDescriptor
@@ -116,10 +123,10 @@ struct LifetimeDescriptor {
116123
return value.Ordered.index;
117124
}
118125

119-
bool isAddressable() const {
126+
IsAddressable_t isAddressable() const {
120127
return kind == DescriptorKind::Ordered
121128
? value.Ordered.isAddress
122-
: false;
129+
: IsNotAddressable;
123130
}
124131

125132
DescriptorKind getDescriptorKind() const { return kind; }
@@ -216,6 +223,8 @@ class LifetimeDependenceInfo {
216223
IndexSubset *scopeLifetimeParamIndices;
217224
llvm::PointerIntPair<IndexSubset *, 1, bool>
218225
addressableParamIndicesAndImmortal;
226+
IndexSubset *conditionallyAddressableParamIndices;
227+
219228
unsigned targetIndex;
220229

221230
static LifetimeDependenceInfo getForIndex(AbstractFunctionDecl *afd,
@@ -249,16 +258,23 @@ class LifetimeDependenceInfo {
249258
IndexSubset *scopeLifetimeParamIndices,
250259
unsigned targetIndex, bool isImmortal,
251260
// set during SIL type lowering
252-
IndexSubset *addressableParamIndices = nullptr)
261+
IndexSubset *addressableParamIndices = nullptr,
262+
IndexSubset *conditionallyAddressableParamIndices = nullptr)
253263
: inheritLifetimeParamIndices(inheritLifetimeParamIndices),
254264
scopeLifetimeParamIndices(scopeLifetimeParamIndices),
255265
addressableParamIndicesAndImmortal(addressableParamIndices, isImmortal),
266+
conditionallyAddressableParamIndices(conditionallyAddressableParamIndices),
256267
targetIndex(targetIndex) {
257268
assert(this->isImmortal() || inheritLifetimeParamIndices ||
258269
scopeLifetimeParamIndices);
259270
assert(!inheritLifetimeParamIndices ||
260271
!inheritLifetimeParamIndices->isEmpty());
261272
assert(!scopeLifetimeParamIndices || !scopeLifetimeParamIndices->isEmpty());
273+
assert((!conditionallyAddressableParamIndices
274+
|| (addressableParamIndices
275+
&& conditionallyAddressableParamIndices
276+
->isSubsetOf(addressableParamIndices)))
277+
&& "conditionally-addressable params not a subset of addressable params?");
262278
}
263279

264280
operator bool() const { return !empty(); }
@@ -286,9 +302,26 @@ class LifetimeDependenceInfo {
286302

287303
IndexSubset *getScopeIndices() const { return scopeLifetimeParamIndices; }
288304

305+
/// Return the set of parameters which have addressable dependencies.
306+
///
307+
/// This indicates that any dependency on the parameter value is dependent
308+
/// not only on the value, but the memory location of a particular instance
309+
/// of the value.
289310
IndexSubset *getAddressableIndices() const {
290311
return addressableParamIndicesAndImmortal.getPointer();
291312
}
313+
/// Return the set of parameters which may have addressable dependencies
314+
/// depending on the type of the parameter.
315+
///
316+
/// Generic parameters need to be conservatively treated as addressable in
317+
/// situations where the substituted type may end up being addressable-for-
318+
/// dependencies. If substitution at a call site or specialization results
319+
/// in the type becoming concretely non-addressable-for-dependencies,
320+
/// then the lifetime dependency can be considered a normal value
321+
/// dependency.
322+
IndexSubset *getConditionallyAddressableIndices() const {
323+
return conditionallyAddressableParamIndices;
324+
}
292325

293326
bool checkInherit(int index) const {
294327
return inheritLifetimeParamIndices

lib/AST/LifetimeDependence.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ getLifetimeDependenceFor(ArrayRef<LifetimeDependenceInfo> lifetimeDependencies,
5050
std::string LifetimeDependenceInfo::getString() const {
5151
std::string lifetimeDependenceString = "@lifetime(";
5252
auto addressable = getAddressableIndices();
53+
auto condAddressable = getConditionallyAddressableIndices();
5354

5455
auto getSourceString = [&](IndexSubset *bitvector, StringRef kind) {
5556
std::string result;
@@ -61,7 +62,11 @@ std::string LifetimeDependenceInfo::getString() const {
6162
}
6263
result += kind;
6364
if (addressable && addressable->contains(i)) {
64-
result += "address ";
65+
if (condAddressable && condAddressable->contains(i)) {
66+
result += "address_for_deps ";
67+
} else {
68+
result += "address ";
69+
}
6570
}
6671
result += std::to_string(i);
6772
isFirstSetBit = false;
@@ -452,6 +457,7 @@ std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::fromDependsOn(
452457
SmallBitVector inheritLifetimeParamIndices(capacity);
453458
SmallBitVector scopeLifetimeParamIndices(capacity);
454459
SmallBitVector addressableLifetimeParamIndices(capacity);
460+
SmallBitVector conditionallyAddressableLifetimeParamIndices(capacity);
455461

456462
auto updateLifetimeDependenceInfo = [&](LifetimeDescriptor descriptor,
457463
unsigned paramIndexToSet,
@@ -494,8 +500,16 @@ std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::fromDependsOn(
494500
if (updateLifetimeDependenceInfo(source, index, paramConvention)) {
495501
return std::nullopt;
496502
}
497-
if (source.isAddressable()) {
503+
switch (source.isAddressable()) {
504+
case LifetimeDescriptor::IsNotAddressable:
505+
break;
506+
case LifetimeDescriptor::IsConditionallyAddressable:
507+
conditionallyAddressableLifetimeParamIndices.set(index);
508+
addressableLifetimeParamIndices.set(index);
509+
break;
510+
case LifetimeDescriptor::IsAddressable:
498511
addressableLifetimeParamIndices.set(index);
512+
break;
499513
}
500514
break;
501515
}
@@ -523,6 +537,9 @@ std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::fromDependsOn(
523537
/*isImmortal*/ false,
524538
addressableLifetimeParamIndices.any()
525539
? IndexSubset::get(ctx, addressableLifetimeParamIndices)
540+
: nullptr,
541+
conditionallyAddressableLifetimeParamIndices.any()
542+
? IndexSubset::get(ctx, conditionallyAddressableLifetimeParamIndices)
526543
: nullptr);
527544
}
528545

lib/Parse/ParseDecl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,7 +2517,7 @@ parseLifetimeDescriptor(Parser &P,
25172517
// In SIL, lifetimes explicitly state whether they are dependent on a
25182518
// memory location in addition to the value stored at that location.
25192519
if (P.isInSILMode()
2520-
&& name.str() == "address"
2520+
&& (name.str() == "address" || name.str() == "address_for_deps")
25212521
&& P.Tok.is(tok::integer_literal)) {
25222522
SourceLoc orderedLoc;
25232523
unsigned index;
@@ -2527,7 +2527,9 @@ parseLifetimeDescriptor(Parser &P,
25272527
return std::nullopt;
25282528
}
25292529
return LifetimeDescriptor::forOrdered(index, lifetimeDependenceKind, loc,
2530-
/*addressable*/ true);
2530+
name.str() == "address_for_deps"
2531+
? LifetimeDescriptor::IsConditionallyAddressable
2532+
: LifetimeDescriptor::IsAddressable);
25312533
}
25322534

25332535
return LifetimeDescriptor::forNamed(name, lifetimeDependenceKind, loc);

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,7 @@ class DestructureInputs {
15781578
SmallVectorImpl<SILParameterInfo> &Inputs;
15791579
SmallVectorImpl<int> &ParameterMap;
15801580
SmallBitVector &AddressableLoweredParameters;
1581+
SmallBitVector &ConditionallyAddressableLoweredParameters;
15811582
unsigned NextOrigParamIndex = 0;
15821583

15831584
void addLoweredParameter(SILParameterInfo parameter,
@@ -1593,11 +1594,13 @@ class DestructureInputs {
15931594
std::optional<ActorIsolation> isolationInfo,
15941595
SmallVectorImpl<SILParameterInfo> &inputs,
15951596
SmallVectorImpl<int> &parameterMap,
1596-
SmallBitVector &addressableParams)
1597-
: expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign),
1598-
IsolationInfo(isolationInfo), Inputs(inputs),
1599-
ParameterMap(parameterMap),
1600-
AddressableLoweredParameters(addressableParams)
1597+
SmallBitVector &addressableParams,
1598+
SmallBitVector &conditionallyAddressableParams)
1599+
: expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign),
1600+
IsolationInfo(isolationInfo), Inputs(inputs),
1601+
ParameterMap(parameterMap),
1602+
AddressableLoweredParameters(addressableParams),
1603+
ConditionallyAddressableLoweredParameters(conditionallyAddressableParams)
16011604
{}
16021605

16031606
void destructure(AbstractionPattern origType,
@@ -1768,7 +1771,9 @@ class DestructureInputs {
17681771

17691772
// Any parameters not yet marked addressable shouldn't be.
17701773
assert(AddressableLoweredParameters.size() <= ParameterMap.size());
1774+
assert(ConditionallyAddressableLoweredParameters.size() <= ParameterMap.size());
17711775
AddressableLoweredParameters.resize(ParameterMap.size(), false);
1776+
ConditionallyAddressableLoweredParameters.resize(ParameterMap.size(), false);
17721777
}
17731778

17741779
void visit(AbstractionPattern origType, AnyFunctionType::Param substParam,
@@ -1809,8 +1814,8 @@ class DestructureInputs {
18091814
if (origFlags.isAddressable()) {
18101815
origType = AbstractionPattern::getOpaque();
18111816

1812-
// Remember that this lowered parameter is addressable in the
1813-
// addressable parameters vector.
1817+
// Remember that this lowered parameter is unconditionally addressable in
1818+
// the addressable parameters vector.
18141819
AddressableLoweredParameters.resize(ParameterMap.size() + 1, false);
18151820
AddressableLoweredParameters[ParameterMap.size()] = true;
18161821
} else if (hasScopedDependency) {
@@ -1821,10 +1826,14 @@ class DestructureInputs {
18211826
if (initialSubstTL.getRecursiveProperties().isAddressableForDependencies()) {
18221827
origType = AbstractionPattern::getOpaque();
18231828

1824-
// Remember that this lowered parameter is addressable in the
1825-
// addressable parameters vector.
1829+
// Remember that this lowered parameter is conditionally addressable in
1830+
// the addressable parameters vector.
18261831
AddressableLoweredParameters.resize(ParameterMap.size() + 1, false);
18271832
AddressableLoweredParameters[ParameterMap.size()] = true;
1833+
1834+
ConditionallyAddressableLoweredParameters
1835+
.resize(ParameterMap.size() + 1, false);
1836+
ConditionallyAddressableLoweredParameters[ParameterMap.size()] = true;
18281837
}
18291838
}
18301839

@@ -2565,6 +2574,7 @@ static CanSILFunctionType getSILFunctionType(
25652574
SmallVector<SILParameterInfo, 8> inputs;
25662575
SmallVector<int, 8> parameterMap;
25672576
SmallBitVector addressableParams;
2577+
SmallBitVector conditionallyAddressableParams;
25682578
{
25692579
std::optional<ActorIsolation> actorIsolation;
25702580
if (constant) {
@@ -2587,7 +2597,9 @@ static CanSILFunctionType getSILFunctionType(
25872597
}
25882598
DestructureInputs destructurer(expansionContext, TC, conventions,
25892599
foreignInfo, actorIsolation, inputs,
2590-
parameterMap, addressableParams);
2600+
parameterMap,
2601+
addressableParams,
2602+
conditionallyAddressableParams);
25912603
destructurer.destructure(origType, substFnInterfaceType.getParams(),
25922604
extInfoBuilder, unimplementable);
25932605
}
@@ -2668,11 +2680,19 @@ static CanSILFunctionType getSILFunctionType(
26682680
IndexSubset *addressableSet = addressableDeps.any()
26692681
? IndexSubset::get(TC.Context, addressableDeps)
26702682
: nullptr;
2683+
2684+
SmallBitVector condAddressableDeps = scopeIndicesSet
2685+
? scopeIndicesSet->getBitVector() & conditionallyAddressableParams
2686+
: SmallBitVector(1, false);
2687+
IndexSubset *condAddressableSet = condAddressableDeps.any()
2688+
? IndexSubset::get(TC.Context, condAddressableDeps)
2689+
: nullptr;
26712690

26722691
return LifetimeDependenceInfo(inheritIndicesSet,
26732692
scopeIndicesSet,
26742693
target, /*immortal*/ false,
2675-
addressableSet);
2694+
addressableSet,
2695+
condAddressableSet);
26762696
};
26772697
// Lower parameter dependencies.
26782698
for (unsigned i = 0; i < parameterMap.size(); ++i) {
@@ -2759,10 +2779,12 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor(
27592779
std::optional<ActorIsolation> actorIsolation; // For now always null.
27602780
SmallVector<int, 8> unusedParameterMap;
27612781
SmallBitVector unusedAddressableParams;
2782+
SmallBitVector unusedConditionalAddressableParams;
27622783
DestructureInputs destructurer(context, TC, conventions, foreignInfo,
27632784
actorIsolation, inputs,
27642785
unusedParameterMap,
2765-
unusedAddressableParams);
2786+
unusedAddressableParams,
2787+
unusedConditionalAddressableParams);
27662788
destructurer.destructure(
27672789
origType, substAccessorType.getParams(),
27682790
extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin),

test/SIL/lifetime_dependence_generics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public func test(pview: borrowing PView) -> View {
3434
return pview.getDefault()
3535
}
3636

37-
// CHECK-LABEL: sil hidden @$s28lifetime_dependence_generics1PPAAE10getDefault1EQzyF : $@convention(method) <Self where Self : P> (@in_guaranteed Self) -> @lifetime(borrow address 0) @out Self.E {
37+
// CHECK-LABEL: sil hidden @$s28lifetime_dependence_generics1PPAAE10getDefault1EQzyF : $@convention(method) <Self where Self : P> (@in_guaranteed Self) -> @lifetime(borrow address_for_deps 0) @out Self.E {
3838

3939
// CHECK-LABEL: sil hidden @$s28lifetime_dependence_generics5PViewV4getEAA4ViewVyF : $@convention(method) (PView) -> @lifetime(immortal) @owned View {
4040

test/SILGen/lifetime_dependence_lowering.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,21 @@ struct Butt {
9090
@_addressableForDependencies
9191
struct AddressableForDeps {
9292
// CHECK-LABEL: sil{{.*}} @$s{{.*}}6test16{{.*}} : $
93-
// CHECK-SAME: -> @lifetime(borrow address 3) @owned Foo
93+
// CHECK-SAME: -> @lifetime(borrow address_for_deps 3) @owned Foo
9494
@lifetime(borrow self)
9595
func test16(tuple: (AddressableForDeps, AddressableForDeps),
9696
other: AddressableForDeps) -> Foo {}
9797

9898
// The dependency makes the tuple pass as a single indirect argument.
9999
// CHECK-LABEL: sil{{.*}} @$s{{.*}}6test17{{.*}} : $
100-
// CHECK-SAME: -> @lifetime(borrow address 0) @owned Foo
100+
// CHECK-SAME: -> @lifetime(borrow address_for_deps 0) @owned Foo
101101
@lifetime(borrow tuple)
102102
func test17(tuple: (AddressableForDeps, AddressableForDeps),
103103
other: AddressableForDeps) -> Foo {}
104104

105105
// The tuple destructures as usual, but `other` is passed indirectly.
106106
// CHECK-LABEL: sil{{.*}} @$s{{.*}}6test18{{.*}} : $
107-
// CHECK-SAME: -> @lifetime(borrow address 2) @owned Foo
107+
// CHECK-SAME: -> @lifetime(borrow address_for_deps 2) @owned Foo
108108
@lifetime(borrow other)
109109
func test18(tuple: (AddressableForDeps, AddressableForDeps),
110110
other: AddressableForDeps) -> Foo {}

0 commit comments

Comments
 (0)