Skip to content

Commit 28bad2b

Browse files
authored
Merge pull request swiftlang#24738 from atrick/simplify-accessed-storage-proj
2 parents d4e6b58 + c1bda8f commit 28bad2b

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)