Skip to content

Commit b90007a

Browse files
committed
Update and reimplement AddressLowering pass (for SIL opaque values).
Merge the AddressLowering pass from its old development branch and update it so we can begin incrementally enabling it under a flag. This has been reimplemented for simplicity. There's no point in looking at the old code.
1 parent 2907c61 commit b90007a

19 files changed

+4579
-1795
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,10 @@ class SILBuilder {
266266
void clearInsertionPoint() { BB = nullptr; }
267267

268268
/// setInsertionPoint - Set the insertion point.
269-
void setInsertionPoint(SILBasicBlock *BB, SILBasicBlock::iterator InsertPt) {
269+
void setInsertionPoint(SILBasicBlock *BB, SILBasicBlock::iterator insertPt) {
270270
this->BB = BB;
271-
this->InsertPt = InsertPt;
272-
if (InsertPt == BB->end())
273-
return;
271+
this->InsertPt = insertPt;
272+
assert(insertPt == BB->end() || insertPt->getParent() == BB);
274273
}
275274

276275
/// setInsertionPoint - Set the insertion point to insert before the specified

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 2679 additions & 1035 deletions
Large diffs are not rendered by default.
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
//===--- AddressLowering.h - Lower SIL address-only types. ----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/SIL/SILArgument.h"
14+
#include "swift/SIL/SILInstruction.h"
15+
#include "swift/SIL/SILValue.h"
16+
#include "llvm/ADT/DenseMap.h"
17+
18+
namespace swift {
19+
20+
/// Track a value's storage. Stages in the storage life-cycle:
21+
///
22+
/// 1. Unallocated
23+
///
24+
/// 2. Allocated. Either (a) 'storageAddress' is an alloc_stack, or (b)
25+
/// 'projectedStorageID' refers to a different ValueStorage, which recursively
26+
/// leads to a valid 'storageAddress'.
27+
///
28+
/// 3. Materialized. 'storageAddress' is valid. Address projections have been
29+
/// emitted at the point that this value is defined.
30+
///
31+
/// 4. Rewritten. The definition of this address-only value is fully translated
32+
/// into lowered SIL. Instructions are typically materialized and rewritten at
33+
/// the same time. A indirect result, however, is materialized as soon as its
34+
/// alloc_stack is emitted, but only rewritten once the call itself is
35+
/// rewritten.
36+
///
37+
/// A projection may project out of an operand's definition (def-projection).
38+
/// After allocation, before materialization or rewriting, we may have:
39+
///
40+
/// %result_addr = alloc_stack // storage for %result
41+
/// %result = apply () -> @out T
42+
/// %extract = struct_extact %result // def-projection of %result
43+
///
44+
/// Or, a projection may project into a composing use (use-projection):
45+
///
46+
/// %struct_addr = alloc_stack // storage for %struct
47+
/// %result = apply () -> @out T // use-projection of %struct at operand #0
48+
/// %struct = struct %result
49+
///
50+
/// A phi-projection is a use projection that projects its entire value
51+
/// through a phi rather than into a composing use. It has an invalid
52+
/// 'projectedOperandNum'.
53+
///
54+
/// Operations that destructively resuse storage (open_existential_value,
55+
/// unchecked_enum_data, and switch_enum), are not considered storage
56+
/// projections. Instead, these values have no ValueStorage but are rewritten to
57+
/// directly reuse their operand's storage.
58+
///
59+
/// To materialize projections, address lowering follows the original def-use
60+
/// edges for address-only values. Consequently, values that have storage cannot
61+
/// be removed from SIL or from the storage map until rewriting is
62+
/// complete. Mapped values can, however, be substituted on-the-fly by emitting
63+
/// a place-holder value and updating the map entry. This works because the
64+
/// value storage map holds no direct references to any SIL entities, such as
65+
/// Operands or SILValues.
66+
struct ValueStorage {
67+
enum : uint32_t { InvalidID = uint32_t(~0) };
68+
enum : uint16_t { InvalidOper = uint16_t(~0) };
69+
70+
/// The final address of this storage after rewriting the SIL. For values
71+
/// linked to their own storage, this is set during storage allocation to an
72+
/// alloc_stack or indirect function argument. For projections, it is only set
73+
/// after materialization (during instruction rewriting).
74+
SILValue storageAddress;
75+
76+
/// When either isDefProjection or isUseProjection is set, this refers to the
77+
/// storage whose "def" this value projects out of or whose operand this
78+
/// storage projects into via its "use.
79+
uint32_t projectedStorageID;
80+
81+
/// For use-projections, identifies the operand index of the composing use.
82+
/// Only valid for non-phi use projections.
83+
uint16_t projectedOperandNum;
84+
85+
/// Projection out of a storage def. e.g. this value is a destructure.
86+
unsigned isDefProjection : 1;
87+
88+
/// Projection into a composing use or phi. e.g. this value is used by a
89+
/// struct, tuple, enum, or branch.
90+
unsigned isUseProjection : 1;
91+
92+
// The definition of this value is fully translated to lowered SIL.
93+
unsigned isRewritten : 1;
94+
95+
// This is a use-projection into an enum. Tracked to avoid projecting enums
96+
// across phis, which would result in piecewise initialization.
97+
unsigned initializesEnum : 1;
98+
99+
ValueStorage() { clear(); }
100+
101+
void clear() {
102+
storageAddress = SILValue();
103+
projectedStorageID = InvalidID;
104+
projectedOperandNum = InvalidOper;
105+
isUseProjection = false;
106+
isDefProjection = false;
107+
isRewritten = false;
108+
initializesEnum = false;
109+
}
110+
111+
bool isAllocated() const {
112+
return storageAddress || isUseProjection || isDefProjection;
113+
}
114+
115+
bool isProjection() const { return isUseProjection || isDefProjection; }
116+
117+
bool isPhiProjection() const {
118+
return isUseProjection && projectedOperandNum == InvalidOper;
119+
}
120+
121+
bool isComposingUseProjection() const {
122+
return isUseProjection && projectedOperandNum != InvalidOper;
123+
}
124+
125+
void markRewritten() {
126+
assert(storageAddress);
127+
isRewritten = true;
128+
}
129+
130+
SILValue getMaterializedAddress() const {
131+
assert(isRewritten && "storage has not been materialized");
132+
return storageAddress;
133+
}
134+
};
135+
136+
/// Map each opaque/resilient SILValue to its abstract storage.
137+
/// Iteration guarantees RPO order.
138+
///
139+
/// Mapped values are expected to be created in a single RPO pass. "erase" is
140+
/// unsupported. Values must be replaced using 'replaceValue()'.
141+
class ValueStorageMap {
142+
struct ValueStoragePair {
143+
SILValue value;
144+
ValueStorage storage;
145+
ValueStoragePair(SILValue v, ValueStorage s) : value(v), storage(s) {}
146+
};
147+
typedef std::vector<ValueStoragePair> ValueVector;
148+
// Hash of values to ValueVector indices.
149+
typedef llvm::DenseMap<SILValue, unsigned> ValueHashMap;
150+
151+
ValueVector valueVector;
152+
ValueHashMap valueHashMap;
153+
154+
// True after valueVector is done growing, so ValueStorage references will no
155+
// longer be invalidated.
156+
SWIFT_ASSERT_ONLY_DECL(bool stableStorage = false);
157+
158+
public:
159+
bool empty() const { return valueVector.empty(); }
160+
161+
void clear() {
162+
valueVector.clear();
163+
valueHashMap.clear();
164+
}
165+
166+
/// Iterate over value storage in RPO order. Once we begin erasing
167+
/// instructions, some entries could become invalid. ValueStorage validity can
168+
/// be checked with valueStorageMap.contains(value).
169+
ValueVector::iterator begin() { return valueVector.begin(); }
170+
171+
ValueVector::iterator end() { return valueVector.end(); }
172+
173+
ValueVector::reverse_iterator rbegin() { return valueVector.rbegin(); }
174+
175+
ValueVector::reverse_iterator rend() { return valueVector.rend(); }
176+
177+
bool contains(SILValue value) const {
178+
return valueHashMap.find(value) != valueHashMap.end();
179+
}
180+
181+
unsigned getOrdinal(SILValue value) const {
182+
auto hashIter = valueHashMap.find(value);
183+
assert(hashIter != valueHashMap.end() && "Missing SILValue");
184+
return hashIter->second;
185+
}
186+
187+
ValueStorage &getStorage(SILValue value) {
188+
return valueVector[getOrdinal(value)].storage;
189+
}
190+
const ValueStorage &getStorage(SILValue value) const {
191+
return valueVector[getOrdinal(value)].storage;
192+
}
193+
194+
const ValueStorage *getStorageOrNull(SILValue value) const {
195+
auto iter = valueHashMap.find(value);
196+
if (iter == valueHashMap.end())
197+
return nullptr;
198+
199+
return &valueVector[iter->second].storage;
200+
}
201+
202+
void setStable() { SWIFT_ASSERT_ONLY(stableStorage = true); }
203+
204+
/// Given storage for a projection, return the projected storage by following
205+
/// single level of projected storage. The returned storage may
206+
/// recursively be a another projection.
207+
ValueStoragePair &getProjectedStorage(const ValueStorage &storage) {
208+
assert(storage.isProjection());
209+
return valueVector[storage.projectedStorageID];
210+
}
211+
212+
/// Return the non-projection storage that the given storage ultimately refers
213+
/// to by following all projections. After allocation, this storage always has
214+
/// a valid address.
215+
const ValueStorage &getBaseStorage(const ValueStorage &storage) {
216+
if (storage.isDefProjection || storage.isUseProjection)
217+
return getBaseStorage(getProjectedStorage(storage).storage);
218+
219+
return storage;
220+
}
221+
222+
/// Return the non-projection storage that the given storage ultimately refers
223+
/// to by following all projections.
224+
const ValueStorage &getBaseStorage(SILValue value) {
225+
return getBaseStorage(getStorage(value));
226+
}
227+
228+
/// Return the non-projection storage that this storage refers to. If this
229+
/// storage holds an Enum or any intermediate storage that projects into this
230+
/// storage holds an Enum, then return nullptr.
231+
const ValueStorage *getNonEnumBaseStorage(const ValueStorage &storage) {
232+
if (storage.initializesEnum)
233+
return nullptr;
234+
235+
if (storage.isUseProjection) {
236+
auto &storageAndValue = getProjectedStorage(storage);
237+
return getNonEnumBaseStorage(storageAndValue.storage);
238+
}
239+
assert(!storage.isDefProjection && "def projections should not reach here");
240+
return &storage;
241+
}
242+
243+
/// Return the non-projection storage that this storage refers to, or nullptr
244+
/// if \p allowInitEnum is true and the storage initializes an Enum.
245+
const ValueStorage *getBaseStorage(SILValue value, bool allowInitEnum) {
246+
if (allowInitEnum)
247+
return &getBaseStorage(value);
248+
249+
return getNonEnumBaseStorage(getStorage(value));
250+
}
251+
252+
/// Insert a value in the map, creating a ValueStorage object for it. This
253+
/// must be called in RPO order.
254+
ValueStorage &insertValue(SILValue value);
255+
256+
/// Replace a value that is mapped to storage with another value. This allows
257+
/// limited rewritting of original address-only values. For example, block
258+
/// arguments can be replaced with fake loads in order to rewrite their
259+
/// corresponding terminator.
260+
void replaceValue(SILValue oldValue, SILValue newValue);
261+
262+
/// Record a storage projection from the source of the given operand into its
263+
/// use (e.g. struct_extract, tuple_extract, switch_enum).
264+
void recordDefProjection(Operand *oper, SILValue projectedValue);
265+
266+
/// Record a storage projection from the use of the given operand into the
267+
/// operand's source. (e.g. Any value used by a struct, tuple, or enum may
268+
/// project storage into its use).
269+
void recordComposingUseProjection(Operand *oper, SILValue userValue);
270+
271+
// Mark a phi operand value as coalesced with the phi storage.
272+
void recordPhiUseProjection(Operand *oper, SILPhiArgument *phi);
273+
274+
/// Return true \p oper projects into its use's aggregate storage.
275+
bool isComposingUseProjection(Operand *oper) const;
276+
277+
#ifndef NDEBUG
278+
void dump();
279+
#endif
280+
};
281+
282+
} // namespace swift

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ target_sources(swiftSILOptimizer PRIVATE
44
AddressLowering.cpp
55
CapturePromotion.cpp
66
ClosureLifetimeFixup.cpp
7+
PhiStorageOptimizer.cpp
78
ConstantPropagation.cpp
89
DefiniteInitialization.cpp
910
DIMemoryUseCollector.cpp

0 commit comments

Comments
 (0)