Skip to content

Commit 604150c

Browse files
committed
[AddressLowering] Place projections below types.
Projections may involve opened archetypes. They must be dominated by the instructions which open those archetypes. When determining the earliest storage point for a value and the point at which to insert its projections, look for such obstructions in the projection chain. The first one found is the earliest storage point.
1 parent c9d8429 commit 604150c

File tree

3 files changed

+111
-6
lines changed

3 files changed

+111
-6
lines changed

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,13 @@ struct AddressLoweringState {
571571
void getDominandsForUseProjection(SILValue userValue,
572572
SmallVectorImpl<SILValue> &dominands) const;
573573

574+
/// Finds and caches the latest opening instruction of the type of the value
575+
/// in \p pair.
576+
///
577+
/// @returns nullable instruction
578+
SILInstruction *
579+
getLatestOpeningInst(const ValueStorageMap::ValueStoragePair *) const;
580+
574581
/// The latest instruction which opens an archetype involved in the indicated
575582
/// type.
576583
///
@@ -1256,6 +1263,14 @@ void OpaqueStorageAllocation::allocateValue(SILValue value) {
12561263
// this value's storage with a branch use.
12571264
createStackAllocationStorage(value);
12581265
}
1266+
SILInstruction *AddressLoweringState::getLatestOpeningInst(
1267+
const ValueStorageMap::ValueStoragePair *pair) const {
1268+
if (!pair->storage.latestOpeningInst.has_value()) {
1269+
auto *loi = getLatestOpeningInst(pair->value->getType());
1270+
pair->storage.latestOpeningInst = {loi};
1271+
}
1272+
return pair->storage.latestOpeningInst.value();
1273+
}
12591274

12601275
void AddressLoweringState::getDominandsForUseProjection(
12611276
SILValue userValue, SmallVectorImpl<SILValue> &dominands) const {
@@ -1283,6 +1298,12 @@ void AddressLoweringState::getDominandsForUseProjection(
12831298
assert(storage.isUseProjection || !storage.isProjection());
12841299
assert(!(storage.isProjection() && storage.storageAddress) &&
12851300
"projections have not yet been materialized!?");
1301+
if (auto *loi = getLatestOpeningInst(pair)) {
1302+
// In order for an opaque value to reuse the storage of some recursive
1303+
// aggregate, it must dominate the instructions that open archetypes that
1304+
// occur at every layer of the aggregation.
1305+
dominands.push_back(cast<SingleValueInstruction>(loi));
1306+
}
12861307
if (!storage.isProjection()) {
12871308
// Reached the bottom of the projection tower. There must be storage.
12881309
assert(storage.storageAddress);
@@ -1348,13 +1369,15 @@ bool OpaqueStorageAllocation::findProjectionIntoUseImpl(
13481369
continue;
13491370
}
13501371

1351-
// Recurse through all storage projections to find the point where the
1352-
// storage has been allocated.
1372+
// Recurse through all storage projections to find (1) the point where the
1373+
// storage has been allocated and (2) any opening instructions involved in
1374+
// any of those projections' types.
13531375
//
1354-
// The base storage address must dominate `incomingValues` because the
1355-
// address projection for each `incomingValue` must be materialized no
1356-
// later than at `incomingValue->getDefiningInsertionPoint()` (but perhaps
1357-
// earlier, see getProjectionInsertionPoint).
1376+
// The base storage address and all of the opened types used by the
1377+
// projections must dominate `incomingValues` because the address
1378+
// projections for each `incomingValue` must be materialized no later than
1379+
// at `incomingValue->getDefiningInsertionPoint()` (but perhaps earlier,
1380+
// see getProjectionInsertionPoint).
13581381
SmallVector<SILValue, 4> dominands;
13591382
pass.getDominandsForUseProjection(userValue, dominands);
13601383

@@ -1830,6 +1853,7 @@ AddressMaterialization::materializeProjectionIntoUse(Operand *operand,
18301853

18311854
SILInstruction *AddressMaterialization::getProjectionInsertionPoint(
18321855
SILValue userValue, AddressLoweringState &pass) {
1856+
SILInstruction *latestOpeningInst = nullptr;
18331857
SILInstruction *retval = userValue->getDefiningInsertionPoint();
18341858
for (auto *pair : pass.valueStorageMap.getProjections(userValue)) {
18351859
auto const &storage = pair->storage;
@@ -1838,8 +1862,33 @@ SILInstruction *AddressMaterialization::getProjectionInsertionPoint(
18381862
retval = storage.storageAddress->getNextInstruction();
18391863
break;
18401864
}
1865+
// It's necessary to consider obstructions at every level of aggregation*
1866+
// because there is no ordering among the opening instructions which
1867+
// define the types used in the aggregate.
1868+
//
1869+
// * Levels above the first projection for which storage has already been
1870+
// allocated, however, do not need to be considered _here_ because they
1871+
// were already considered when determining where to create that
1872+
// instruction, either in getProjectionInsertionPoint or in
1873+
// OpaqueStorageAllocation::createStackAllocation.
1874+
if (auto *loi = pass.getLatestOpeningInst(pair)) {
1875+
if (latestOpeningInst) {
1876+
if (pass.domInfo->dominates(loi, latestOpeningInst)) {
1877+
continue;
1878+
1879+
assert(pass.domInfo->dominates(latestOpeningInst, loi));
1880+
}
1881+
}
1882+
latestOpeningInst = loi->getNextInstruction();
1883+
}
18411884
}
18421885
assert(retval);
1886+
if (latestOpeningInst) {
1887+
if (pass.domInfo->dominates(retval, latestOpeningInst))
1888+
retval = latestOpeningInst;
1889+
else
1890+
assert(pass.domInfo->dominates(latestOpeningInst, retval));
1891+
}
18431892
return retval;
18441893
}
18451894

lib/SILOptimizer/Mandatory/AddressLowering.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ struct ValueStorage {
128128
/// after materialization (during instruction rewriting).
129129
SILValue storageAddress;
130130

131+
/// The latest instruction which opens an archetype involved in the value's
132+
/// type. Just a cache of getLatestOpeningInst(value).
133+
mutable llvm::Optional<SILInstruction *> latestOpeningInst = llvm::None;
134+
131135
/// When either isDefProjection or isUseProjection is set, this refers to the
132136
/// storage whose "def" this value projects out of or whose operand this
133137
/// storage projects into via its "use.
@@ -205,6 +209,7 @@ struct ValueStorage {
205209
/// Mapped values are expected to be created in a single RPO pass. "erase" is
206210
/// unsupported. Values must be replaced using 'replaceValue()'.
207211
class ValueStorageMap {
212+
public:
208213
struct ValueStoragePair {
209214
SILValue value;
210215
ValueStorage storage;
@@ -213,6 +218,8 @@ class ValueStorageMap {
213218
void dump() const;
214219
#endif
215220
};
221+
222+
private:
216223
typedef std::vector<ValueStoragePair> ValueVector;
217224
// Hash of values to ValueVector indices.
218225
typedef llvm::DenseMap<SILValue, unsigned> ValueHashMap;

test/SILOptimizer/address_lowering.sil

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ struct Pair<T> {
5656
var y : T
5757
}
5858

59+
struct Box2<T, U> {
60+
var v1: T
61+
var v2: U
62+
}
63+
5964
enum Mixed<T> {
6065
case i(Int)
6166
case t(T)
@@ -81,9 +86,11 @@ public protocol Comparable {
8186
static func < (lhs: Self, rhs: Self) -> Bool
8287
}
8388

89+
8490
sil [ossa] @unknown : $@convention(thin) () -> ()
8591
sil [ossa] @getT : $@convention(thin) <T> () -> @out T
8692
sil [ossa] @getPair : $@convention(thin) <T> () -> @out Pair<T>
93+
sil [ossa] @getOwned : $@convention(thin) <T : AnyObject> () -> (@owned T)
8794
sil [ossa] @takeGuaranteedObject : $@convention(thin) (@guaranteed AnyObject) -> ()
8895
sil [ossa] @takeIndirectClass : $@convention(thin) (@in_guaranteed C) -> ()
8996
sil [ossa] @takeTuple : $@convention(thin) <τ_0_0> (@in_guaranteed (τ_0_0, C)) -> ()
@@ -1090,6 +1097,48 @@ bb0(%error_exi : @guaranteed $any Error):
10901097
return %tuple : $()
10911098
}
10921099

1100+
// Test a chain of projections multiple nodes of which feature an opened
1101+
// archetype AND the archetype-definining instruction for the first projection
1102+
// whose type has an opened archetype dominates all blocks but the
1103+
// archetype-defining instruction for the SECOND projection does not.
1104+
//
1105+
// This test case demonstrates why getDominandsForUseProjection returns
1106+
// multiple values all of which must dominate each incoming value.
1107+
//
1108+
// CHECK-LABEL: sil [ossa] @f165_testOpenedArchetypsDominance : {{.*}} {
1109+
// CHECK: alloc_stack $(T, T)
1110+
// CHECK-LABEL: } // end sil function 'f165_testOpenedArchetypsDominance'
1111+
sil [ossa] @f165_testOpenedArchetypsDominance : $@convention(thin) <T> () -> () {
1112+
%borrow = function_ref @takeInGuaranteed : $@convention(thin) <T> (@in_guaranteed T) -> ()
1113+
%take = function_ref @takeIn : $@convention(thin) <T> (@in T) -> ()
1114+
%get = function_ref @getT : $@convention(thin) <T> () -> (@out T)
1115+
%getOwned = function_ref @getOwned : $@convention(thin) <T : AnyObject> () -> (@owned T)
1116+
1117+
1118+
%exi = apply %getOwned<any P & C>() : $@convention(thin) <T : AnyObject> () -> (@owned T)
1119+
%exi2 = apply %getOwned<any P & C>() : $@convention(thin) <T : AnyObject> () -> (@owned T)
1120+
%O_1 = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
1121+
%O_2 = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
1122+
%O = tuple (%O_1 : $T, %O_2 : $T)
1123+
%ope = open_existential_ref %exi : $P & C to $@opened("00000000-0000-0000-0000-000000000000", P & C) Self
1124+
cond_br undef, left, right
1125+
1126+
left:
1127+
%ope2 = open_existential_ref %exi2 : $P & C to $@opened("00000000-0000-0000-0000-000000000001", P & C) Self
1128+
%U = struct $Box2<(T, T), @opened("00000000-0000-0000-0000-000000000000", P & C) Self> (%O : $(T, T), %ope : $@opened("00000000-0000-0000-0000-000000000000", P & C) Self)
1129+
%U2 = struct $Box2<Box2<(T, T), @opened("00000000-0000-0000-0000-000000000000", P & C) Self>, @opened("00000000-0000-0000-0000-000000000001", P & C) Self> (%U : $Box2<(T, T), @opened("00000000-0000-0000-0000-000000000000", P & C) Self> , %ope2 : $@opened("00000000-0000-0000-0000-000000000001", P & C) Self)
1130+
destroy_value %U2 : $Box2<Box2<(T, T), @opened("00000000-0000-0000-0000-000000000000", P & C) Self>, @opened("00000000-0000-0000-0000-000000000001", P & C) Self>
1131+
br exit
1132+
right:
1133+
destroy_value %exi2 : $any P & C
1134+
destroy_value %ope : $@opened("00000000-0000-0000-0000-000000000000", P & C) Self
1135+
destroy_value %O : $(T, T)
1136+
br exit
1137+
exit:
1138+
%retval = tuple ()
1139+
return %retval : $()
1140+
}
1141+
10931142
// CHECK-LABEL: sil [ossa] @f170_compare : $@convention(thin) <T where T : Comparable> (@in_guaranteed T, @in_guaranteed T) -> @out T {
10941143
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T):
10951144
// CHECK: [[WT:%.*]] = witness_method $T, #Comparable."<" : <Self where Self : Comparable> (Self.Type) -> (Self, Self) -> Builtin.Int1 : $@convention(witness_method: Comparable) <τ_0_0 where τ_0_0 : Comparable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Builtin.Int1

0 commit comments

Comments
 (0)