Skip to content

Commit c1bda8f

Browse files
committed
Replace AccessedStorage projection with an index.
Further simplify AccessedStorage. Shrink it to two words. Remove the Projection abstraction and streamline the projection logic.
1 parent 05808b9 commit c1bda8f

File tree

8 files changed

+111
-97
lines changed

8 files changed

+111
-97
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 63 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
#ifndef SWIFT_SIL_MEMACCESSUTILS_H
1616
#define SWIFT_SIL_MEMACCESSUTILS_H
1717

18-
#include "swift/SIL/Projection.h"
1918
#include "swift/SIL/InstructionUtils.h"
19+
#include "swift/SIL/SILArgument.h"
20+
#include "swift/SIL/SILBasicBlock.h"
2021
#include "swift/SIL/SILInstruction.h"
2122
#include "llvm/ADT/DenseMap.h"
2223

@@ -28,31 +29,6 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
2829
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
2930
}
3031

31-
/// Represents the identity of a stored class property as a combination
32-
/// of a base and projection.
33-
class ObjectProjection {
34-
SILValue object;
35-
Projection proj;
36-
37-
public:
38-
ObjectProjection(SILValue object, const Projection &projection)
39-
: object(object), proj(projection) {
40-
assert(object->getType().isObject());
41-
}
42-
43-
SILValue getObject() const { return object; }
44-
45-
const Projection &getProjection() const { return proj; }
46-
47-
bool operator==(const ObjectProjection &other) const {
48-
return object == other.object && proj == other.proj;
49-
}
50-
51-
bool operator!=(const ObjectProjection &other) const {
52-
return object != other.object || proj != other.proj;
53-
}
54-
};
55-
5632
/// Represents the identity of a storage object being accessed.
5733
///
5834
/// AccessedStorage may be one of several kinds of "identified" storage
@@ -107,7 +83,23 @@ class AccessedStorage {
10783
/// of access base. Otherwise, return Unidentified.
10884
static AccessedStorage::Kind classify(SILValue base);
10985

86+
/// Directly create an AccessedStorage for class property access.
87+
static AccessedStorage forClass(SILValue object, unsigned propertyIndex) {
88+
AccessedStorage storage;
89+
storage.initKind(Class, propertyIndex);
90+
storage.value = object;
91+
return storage;
92+
}
93+
11094
protected:
95+
// Checking the storage kind is far more common than other fields. Make sure
96+
// it can be byte load with no shift.
97+
static const int ReservedKindBits = 8;
98+
static_assert(ReservedKindBits >= NumKindBits, "Too many storage kinds.");
99+
100+
static const unsigned InvalidElementIndex =
101+
(1 << (32 - ReservedKindBits)) - 1;
102+
111103
// Form a bitfield that is effectively a union over any pass-specific data
112104
// with the fields used within this class as a common prefix.
113105
//
@@ -122,9 +114,13 @@ class AccessedStorage {
122114
// each begin_access to its storage object, unique access index, and summary
123115
// info for that access.
124116
union {
125-
uint64_t OpaqueBits;
126-
SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, bitmax(NumKindBits, 8),
127-
Kind : bitmax(NumKindBits, 8));
117+
uint64_t opaqueBits;
118+
// elementIndex can overflow while gracefully degrading analysis. For now,
119+
// reserve an absurd number of bits at a nice alignment boundary, but this
120+
// can be reduced.
121+
SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, kind
122+
: ReservedKindBits,
123+
elementIndex : 32 - ReservedKindBits);
128124

129125
// Define bits for use in AccessedStorageAnalysis. Each identified storage
130126
// object is mapped to one instance of this subclass.
@@ -157,59 +153,67 @@ class AccessedStorage {
157153

158154
private:
159155
union {
156+
// For non-class storage, 'value' is the access base. For class storage
157+
// 'value' is the object base, where the access base is the class' stored
158+
// property.
160159
SILValue value;
161-
unsigned paramIndex;
162160
SILGlobalVariable *global;
163-
ObjectProjection objProj;
164161
};
165162

166-
void initKind(Kind k) {
167-
Bits.OpaqueBits = 0;
168-
Bits.AccessedStorage.Kind = k;
163+
void initKind(Kind k, unsigned elementIndex = InvalidElementIndex) {
164+
Bits.opaqueBits = 0;
165+
Bits.AccessedStorage.kind = k;
166+
Bits.AccessedStorage.elementIndex = elementIndex;
167+
}
168+
169+
unsigned getElementIndex() const { return Bits.AccessedStorage.elementIndex; }
170+
void setElementIndex(unsigned elementIndex) {
171+
Bits.AccessedStorage.elementIndex = elementIndex;
169172
}
170173

171174
public:
172175
AccessedStorage() : value() { initKind(Unidentified); }
173176

174177
AccessedStorage(SILValue base, Kind kind);
175178

176-
AccessedStorage(SILValue object, Projection projection)
177-
: objProj(object, projection) {
178-
initKind(Class);
179-
}
180-
181179
// Return true if this is a valid storage object.
182180
operator bool() const { return getKind() != Unidentified || value; }
183181

184-
Kind getKind() const { return static_cast<Kind>(Bits.AccessedStorage.Kind); }
182+
Kind getKind() const { return static_cast<Kind>(Bits.AccessedStorage.kind); }
185183

186184
// Clear any bits reserved for subclass data. Useful for up-casting back to
187185
// the base class.
188-
void resetSubclassData() { initKind(getKind()); }
186+
void resetSubclassData() {
187+
initKind(getKind(), Bits.AccessedStorage.elementIndex);
188+
}
189189

190190
SILValue getValue() const {
191-
assert(getKind() != Argument && getKind() != Global && getKind() != Class);
191+
assert(getKind() != Global && getKind() != Class);
192192
return value;
193193
}
194194

195195
unsigned getParamIndex() const {
196196
assert(getKind() == Argument);
197-
return paramIndex;
197+
return getElementIndex();
198198
}
199199

200-
SILArgument *getArgument(SILFunction *F) const {
200+
SILArgument *getArgument() const {
201201
assert(getKind() == Argument);
202-
return F->getArgument(paramIndex);
202+
return cast<SILArgument>(value);
203203
}
204204

205205
SILGlobalVariable *getGlobal() const {
206206
assert(getKind() == Global);
207207
return global;
208208
}
209209

210-
const ObjectProjection &getObjectProjection() const {
210+
SILValue getObject() const {
211211
assert(getKind() == Class);
212-
return objProj;
212+
return value;
213+
}
214+
unsigned getPropertyIndex() const {
215+
assert(getKind() == Class);
216+
return getElementIndex();
213217
}
214218

215219
/// Return true if the given storage objects have identical storage locations.
@@ -223,16 +227,16 @@ class AccessedStorage {
223227
switch (getKind()) {
224228
case Box:
225229
case Stack:
230+
case Argument:
226231
case Yield:
227232
case Nested:
228233
case Unidentified:
229234
return value == other.value;
230-
case Argument:
231-
return paramIndex == other.paramIndex;
232235
case Global:
233236
return global == other.global;
234237
case Class:
235-
return objProj == other.objProj;
238+
return value == other.value
239+
&& getElementIndex() == other.getElementIndex();
236240
}
237241
}
238242

@@ -286,19 +290,19 @@ class AccessedStorage {
286290
// Classes are not uniquely identified by their base. However, if the
287291
// underling objects have identical types and distinct property indices then
288292
// they are distinct storage locations.
289-
auto &proj = getObjectProjection();
290-
auto &otherProj = other.getObjectProjection();
291-
if (proj.getObject()->getType() == otherProj.getObject()->getType()
292-
&& proj.getProjection() != otherProj.getProjection()) {
293+
if (getObject()->getType() == other.getObject()->getType()
294+
&& getPropertyIndex() != other.getPropertyIndex()) {
293295
return true;
294296
}
295297
return false;
296298
}
297299

298300
/// Returns the ValueDecl for the underlying storage, if it can be
299-
/// determined. Otherwise returns null. For diagnostics and checking via the
300-
/// ValueDecl if we are processing a `let` variable.
301-
const ValueDecl *getDecl(SILFunction *F) const;
301+
/// determined. Otherwise returns null.
302+
///
303+
/// WARNING: This is not a constant-time operation. It is for diagnostics and
304+
/// checking via the ValueDecl if we are processing a `let` variable.
305+
const ValueDecl *getDecl() const;
302306

303307
void print(raw_ostream &os) const;
304308
void dump() const;
@@ -343,8 +347,8 @@ template <> struct DenseMapInfo<swift::AccessedStorage> {
343347
case swift::AccessedStorage::Global:
344348
return DenseMapInfo<void *>::getHashValue(storage.getGlobal());
345349
case swift::AccessedStorage::Class: {
346-
const swift::ObjectProjection &P = storage.getObjectProjection();
347-
return llvm::hash_combine(P.getObject(), P.getProjection());
350+
return llvm::hash_combine(storage.getObject(),
351+
storage.getPropertyIndex());
348352
}
349353
}
350354
llvm_unreachable("Unhandled AccessedStorageKind");

lib/SIL/MemAccessUtils.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#define DEBUG_TYPE "sil-access-utils"
1414

1515
#include "swift/SIL/MemAccessUtils.h"
16+
#include "swift/SIL/ApplySite.h"
1617
#include "swift/SIL/SILGlobalVariable.h"
18+
#include "swift/SIL/SILModule.h"
1719
#include "swift/SIL/SILUndef.h"
1820

1921
using namespace swift;
@@ -81,7 +83,8 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) {
8183
value = base;
8284
break;
8385
case Argument:
84-
paramIndex = cast<SILFunctionArgument>(base)->getIndex();
86+
value = base;
87+
setElementIndex(cast<SILFunctionArgument>(base)->getIndex());
8588
break;
8689
case Global:
8790
if (auto *GAI = dyn_cast<GlobalAddrInst>(base))
@@ -105,13 +108,13 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) {
105108
// dynamically checked, and static analysis will be sufficiently
106109
// conservative given that classes are not "uniquely identified".
107110
auto *REA = cast<RefElementAddrInst>(base);
108-
SILValue Object = stripBorrow(REA->getOperand());
109-
objProj = ObjectProjection(Object, Projection(REA));
111+
value = stripBorrow(REA->getOperand());
112+
setElementIndex(REA->getFieldNo());
110113
}
111114
}
112115
}
113116

114-
const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const {
117+
const ValueDecl *AccessedStorage::getDecl() const {
115118
switch (getKind()) {
116119
case Box:
117120
return cast<AllocBoxInst>(value)->getLoc().getAsASTNode<VarDecl>();
@@ -122,11 +125,12 @@ const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const {
122125
case Global:
123126
return global->getDecl();
124127

125-
case Class:
126-
return objProj.getProjection().getVarDecl(objProj.getObject()->getType());
127-
128+
case Class: {
129+
auto *decl = getObject()->getType().getNominalOrBoundGenericNominal();
130+
return *std::next(decl->getStoredProperties().begin(), getPropertyIndex());
131+
}
128132
case Argument:
129-
return getArgument(F)->getDecl();
133+
return getArgument()->getDecl();
130134

131135
case Yield:
132136
return nullptr;
@@ -173,15 +177,17 @@ void AccessedStorage::print(raw_ostream &os) const {
173177
os << value;
174178
break;
175179
case Argument:
176-
os << "index: " << paramIndex << "\n";
180+
os << value;
177181
break;
178182
case Global:
179183
os << *global;
180184
break;
181185
case Class:
182-
os << objProj.getObject() << " ";
183-
objProj.getProjection().print(os, objProj.getObject()->getType());
184-
os << "\n";
186+
os << getObject();
187+
os << " Field: ";
188+
getDecl()->print(os);
189+
os << " Index: " << getPropertyIndex() << "\n";
190+
break;
185191
}
186192
}
187193

@@ -457,7 +463,7 @@ AccessedStorage swift::findAccessedStorageNonNested(SILValue sourceAddr) {
457463

458464
// Return true if the given access is on a 'let' lvalue.
459465
static bool isLetAccess(const AccessedStorage &storage, SILFunction *F) {
460-
if (auto *decl = dyn_cast_or_null<VarDecl>(storage.getDecl(F)))
466+
if (auto *decl = dyn_cast_or_null<VarDecl>(storage.getDecl()))
461467
return decl->isLet();
462468

463469
// It's unclear whether a global will ever be missing it's varDecl, but

lib/SIL/SILVerifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "swift/AST/Types.h"
2323
#include "swift/Basic/Range.h"
2424
#include "swift/ClangImporter/ClangModule.h"
25+
#include "swift/SIL/ApplySite.h"
26+
#include "swift/SIL/BasicBlockUtils.h"
2527
#include "swift/SIL/DebugUtils.h"
2628
#include "swift/SIL/Dominance.h"
2729
#include "swift/SIL/DynamicCasts.h"
@@ -35,7 +37,6 @@
3537
#include "swift/SIL/SILVTable.h"
3638
#include "swift/SIL/SILVTableVisitor.h"
3739
#include "swift/SIL/SILVisitor.h"
38-
#include "swift/SIL/BasicBlockUtils.h"
3940
#include "swift/SIL/TypeLowering.h"
4041
#include "llvm/ADT/DenseSet.h"
4142
#include "llvm/ADT/PostOrderIterator.h"

lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,15 @@ transformCalleeStorage(const StorageAccessInfo &storage,
234234
case AccessedStorage::Class: {
235235
// If the object's value is an argument, translate it into a value on the
236236
// caller side.
237-
SILValue obj = storage.getObjectProjection().getObject();
237+
SILValue obj = storage.getObject();
238238
if (auto *arg = dyn_cast<SILFunctionArgument>(obj)) {
239239
SILValue argVal = getCallerArg(fullApply, arg->getIndex());
240240
if (argVal) {
241-
auto &proj = storage.getObjectProjection().getProjection();
242-
// Remap the argument source value and inherit the old storage info.
243-
return StorageAccessInfo(AccessedStorage(argVal, proj), storage);
241+
unsigned idx = storage.getPropertyIndex();
242+
// Remap this storage info. The argument source value is now the new
243+
// object. The old storage info is inherited.
244+
return StorageAccessInfo(AccessedStorage::forClass(argVal, idx),
245+
storage);
244246
}
245247
}
246248
// Otherwise, continue to reference the value in the callee because we don't

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation,
542542
unsigned AccessKindForMain =
543543
static_cast<unsigned>(MainAccess.getAccessKind());
544544

545-
if (const ValueDecl *VD = Storage.getDecl(F)) {
545+
if (const ValueDecl *VD = Storage.getDecl()) {
546546
// We have a declaration, so mention the identifier in the diagnostic.
547547
SILType BaseType = FirstAccess.getInstruction()->getType().getAddressType();
548548
SILModule &M = FirstAccess.getInstruction()->getModule();

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) {
353353
// If there is 2+ writes, we can not optimize = (.
354354

355355
bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) {
356-
auto *arg = cast<SILFunctionArgument>(storage.getArgument(&f));
356+
auto *arg = cast<SILFunctionArgument>(storage.getArgument());
357357

358358
// Then check if we have an in_guaranteed argument. In this case, we can
359359
// always optimize load [copy] from this.

0 commit comments

Comments
 (0)