Skip to content

Commit 82eda6e

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 931ae15 commit 82eda6e

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 47 additions & 0 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);
@@ -1826,6 +1847,7 @@ AddressMaterialization::materializeProjectionIntoUse(Operand *operand,
18261847

18271848
SILInstruction *AddressMaterialization::getProjectionInsertionPoint(
18281849
SILValue userValue, AddressLoweringState &pass) {
1850+
SILInstruction *latestOpeningInst = nullptr;
18291851
SILInstruction *retval = userValue->getDefiningInsertionPoint();
18301852
for (auto *pair : pass.valueStorageMap.getProjections(userValue)) {
18311853
auto const &storage = pair->storage;
@@ -1834,8 +1856,33 @@ SILInstruction *AddressMaterialization::getProjectionInsertionPoint(
18341856
retval = storage.storageAddress->getNextInstruction();
18351857
break;
18361858
}
1859+
// It's necessary to consider obstructions at every level of aggregation*
1860+
// because there is no ordering among the opening instructions which
1861+
// define the types used in the aggregate.
1862+
//
1863+
// * Levels above the first projection for which storage has already been
1864+
// allocated, however, does not need to be considered _here_ because it
1865+
// was already considered when determining where to create that
1866+
// instruction, either in getProjectionInsertionPoint or in
1867+
// OpaqueStorageAllocation::createStackAllocation.
1868+
if (auto *loi = pass.getLatestOpeningInst(pair)) {
1869+
if (latestOpeningInst) {
1870+
if (pass.domInfo->dominates(loi, latestOpeningInst)) {
1871+
continue;
1872+
1873+
assert(pass.domInfo->dominates(latestOpeningInst, loi));
1874+
}
1875+
}
1876+
latestOpeningInst = loi->getNextInstruction();
1877+
}
18371878
}
18381879
assert(retval);
1880+
if (latestOpeningInst) {
1881+
if (pass.domInfo->dominates(retval, latestOpeningInst))
1882+
retval = latestOpeningInst;
1883+
else
1884+
assert(pass.domInfo->dominates(latestOpeningInst, retval));
1885+
}
18391886
return retval;
18401887
}
18411888

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)