Skip to content

Commit 5e36a2a

Browse files
committed
Fix an AccessedStorage assert for SIL global variables.
Allow round-tripping access to global variables. Previously, AccessedStorage asserted that global variables were always associated with a VarDecl. This was to ensure that AccessEnforcmentWMO always recognized the global. Failing to recognize access to a global will cause a miscompile. SILGlobalVariable now has all the information needed by SIL. Particularly, the 'isLet' flag. Simply replace VarDecl with SILGlobalVariable in AccessEnforcmentWMO to eliminate the need for the assert.
1 parent a6a330c commit 5e36a2a

File tree

4 files changed

+73
-38
lines changed

4 files changed

+73
-38
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ class AccessBase : public AccessRepresentation {
627627
return findReferenceRoot(getReference());
628628
}
629629

630-
/// Return the global variable being accessed.
630+
/// Return the global variable being accessed. Always valid.
631631
///
632632
/// Precondition: getKind() == Global
633633
SILGlobalVariable *getGlobal() const;

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -955,17 +955,10 @@ AccessStorage::AccessStorage(SILValue base, Kind kind)
955955
}
956956
if (getKind() == AccessBase::Global) {
957957
global = getReferencedGlobal(cast<SingleValueInstruction>(base));
958-
// Require a decl for all formally accessed globals defined in this
959-
// module. AccessEnforcementWMO requires this. Swift globals defined in
960-
// another module either use an addressor, which has Unidentified
961-
// storage. Imported non-Swift globals are accessed via global_addr but have
962-
// no declaration.
963-
assert(global->getDecl() || isa<GlobalAddrInst>(base));
964-
965958
// It's unclear whether a global will ever be missing it's varDecl, but
966959
// technically we only preserve it for debug info. So if we don't have a
967-
// decl, check the flag on SILGlobalVariable, which is guaranteed valid,
968-
setLetAccess(getGlobal()->isLet());
960+
// decl, check the flag on SILGlobalVariable, which is guaranteed valid.
961+
setLetAccess(global->isLet());
969962
return;
970963
}
971964
value = base;
@@ -1001,7 +994,7 @@ const ValueDecl *AccessStorage::getDecl() const {
1001994
return nullptr;
1002995
case Class: {
1003996
// The property index is relative to the VarDecl in ref_element_addr, and
1004-
// can only be reliably determined when the base is avaiable. Without the
997+
// can only be reliably determined when the base is available. Without the
1005998
// base, we can only make a best effort to extract it from the object type,
1006999
// which might not even be a class in the case of bridge objects.
10071000
if (ClassDecl *classDecl =

lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,28 @@ using namespace swift;
6666
using llvm::DenseMap;
6767
using llvm::SmallDenseSet;
6868

69+
using DisjointAccessLocationKey =
70+
llvm::PointerUnion<const VarDecl *, const SILGlobalVariable *>;
71+
6972
// Get the VarDecl that represents the DisjointAccessLocation for the given
7073
// storage and access base. Returns nullptr for any storage that can't be
7174
// partitioned into a disjoint location.
7275
//
7376
// Global storage is expected to be disjoint because identifyFormalAccess may
7477
// only return Unidentified storage for a global variable access if the global
7578
// is defined in a different module.
76-
const VarDecl *
79+
static DisjointAccessLocationKey
7780
getDisjointAccessLocation(AccessStorageWithBase storageAndBase) {
7881
auto storage = storageAndBase.storage;
7982
switch (storage.getKind()) {
83+
case AccessStorage::Class: {
84+
auto *varDecl = cast<VarDecl>(storageAndBase.getDecl());
85+
// For class properties, a VarDecl can always be derived from AccessBase.
86+
assert(varDecl && "no VarDecl for class property");
87+
return varDecl;
88+
}
8089
case AccessStorage::Global:
81-
case AccessStorage::Class:
82-
// Class and Globals are always a VarDecl, but the global decl may have a
83-
// null value for global_addr -> phi.
84-
return cast_or_null<VarDecl>(storageAndBase.getDecl());
90+
return storageAndBase.getAccessBase().getGlobal();
8591
case AccessStorage::Box:
8692
case AccessStorage::Stack:
8793
case AccessStorage::Tail:
@@ -95,6 +101,21 @@ getDisjointAccessLocation(AccessStorageWithBase storageAndBase) {
95101
llvm_unreachable("unhandled kind");
96102
}
97103

104+
static bool isVisibleExternally(DisjointAccessLocationKey key, SILModule *mod) {
105+
if (auto *decl = key.dyn_cast<const VarDecl *>())
106+
return mod->isVisibleExternally(decl);
107+
108+
auto *global = key.get<const SILGlobalVariable *>();
109+
return isPossiblyUsedExternally(global->getLinkage(), mod->isWholeModule());
110+
}
111+
112+
static StringRef getName(DisjointAccessLocationKey key) {
113+
if (auto *decl = key.dyn_cast<const VarDecl *>())
114+
return decl->getNameStr();
115+
116+
return key.get<const SILGlobalVariable *>()->getName();
117+
}
118+
98119
namespace {
99120
// Implements an optimization to remove access markers on disjoint memory
100121
// locations that are never reentrantly accessed. For a given memory location,
@@ -134,7 +155,8 @@ class GlobalAccessRemoval {
134155
BeginAccessSet beginAccessSet;
135156
};
136157

137-
DenseMap<const VarDecl *, DisjointAccessLocationInfo> disjointAccessMap;
158+
DenseMap<DisjointAccessLocationKey, DisjointAccessLocationInfo>
159+
disjointAccessMap;
138160

139161
public:
140162
GlobalAccessRemoval(SILModule &module) : module(module) {}
@@ -143,9 +165,8 @@ class GlobalAccessRemoval {
143165

144166
protected:
145167
void visitInstruction(SILInstruction *I);
146-
void recordAccess(SILInstruction *beginAccess, const VarDecl *decl,
147-
AccessStorage::Kind storageKind,
148-
bool hasNoNestedConflict);
168+
void recordAccess(SILInstruction *beginAccess, DisjointAccessLocationKey key,
169+
AccessStorage::Kind storageKind, bool hasNoNestedConflict);
149170
void removeNonreentrantAccess();
150171
};
151172
} // namespace
@@ -169,15 +190,15 @@ void GlobalAccessRemoval::perform() {
169190
void GlobalAccessRemoval::visitInstruction(SILInstruction *I) {
170191
if (auto *BAI = dyn_cast<BeginAccessInst>(I)) {
171192
auto storageAndBase = AccessStorageWithBase::compute(BAI->getSource());
172-
const VarDecl *decl = getDisjointAccessLocation(storageAndBase);
173-
recordAccess(BAI, decl, storageAndBase.storage.getKind(),
193+
auto key = getDisjointAccessLocation(storageAndBase);
194+
recordAccess(BAI, key, storageAndBase.storage.getKind(),
174195
BAI->hasNoNestedConflict());
175196
return;
176197
}
177198
if (auto *BUAI = dyn_cast<BeginUnpairedAccessInst>(I)) {
178199
auto storageAndBase = AccessStorageWithBase::compute(BUAI->getSource());
179-
const VarDecl *decl = getDisjointAccessLocation(storageAndBase);
180-
recordAccess(BUAI, decl, storageAndBase.storage.getKind(),
200+
auto key = getDisjointAccessLocation(storageAndBase);
201+
recordAccess(BUAI, key, storageAndBase.storage.getKind(),
181202
BUAI->hasNoNestedConflict());
182203
return;
183204
}
@@ -213,21 +234,21 @@ void GlobalAccessRemoval::visitInstruction(SILInstruction *I) {
213234
// key_path instruction somewhere else in the same module (or it must be dead
214235
// code, or only access public properties).
215236
//
216-
// `decl` may be nullptr if the declaration can't be determined from the
237+
// `key` may be nullptr if the variable's identity cannot be determined from the
217238
// access. This is only legal when the access is known to be a local access, not
218239
// a class property or global.
219240
void GlobalAccessRemoval::recordAccess(SILInstruction *beginAccess,
220-
const VarDecl *decl,
241+
DisjointAccessLocationKey key,
221242
AccessStorage::Kind storageKind,
222243
bool hasNoNestedConflict) {
223-
if (!decl || module.isVisibleExternally(decl))
244+
if (key.isNull() || isVisibleExternally(key, &module))
224245
return;
225246

226247
LLVM_DEBUG(if (!hasNoNestedConflict) llvm::dbgs()
227-
<< "Nested conflict on " << decl->getName() << " at"
228-
<< *beginAccess << "\n");
248+
<< "Nested conflict on " << getName(key) << " at" << *beginAccess
249+
<< "\n");
229250

230-
auto accessLocIter = disjointAccessMap.find(decl);
251+
auto accessLocIter = disjointAccessMap.find(key);
231252
if (accessLocIter != disjointAccessMap.end()) {
232253
// Add this begin_access to an existing DisjointAccessLocationInfo.
233254
DisjointAccessLocationInfo &info = accessLocIter->second;
@@ -245,22 +266,21 @@ void GlobalAccessRemoval::recordAccess(SILInstruction *beginAccess,
245266
info.noNestedConflict = hasNoNestedConflict;
246267
if (auto *BA = dyn_cast<BeginAccessInst>(beginAccess))
247268
info.beginAccessSet.insert(BA);
248-
disjointAccessMap.insert(std::make_pair(decl, info));
269+
disjointAccessMap.insert(std::make_pair(key, info));
249270
}
250271

251272
// For each unique storage within this function that is never reentrantly
252273
// accessed, promote all access checks for that storage to static enforcement.
253274
void GlobalAccessRemoval::removeNonreentrantAccess() {
254-
for (auto &declAndInfo : disjointAccessMap) {
255-
const DisjointAccessLocationInfo &info = declAndInfo.second;
275+
for (auto &keyAndInfo : disjointAccessMap) {
276+
const DisjointAccessLocationInfo &info = keyAndInfo.second;
256277
if (!info.noNestedConflict)
257278
continue;
258279

259-
const VarDecl *decl = declAndInfo.first;
260-
LLVM_DEBUG(llvm::dbgs() << "Eliminating all formal access on "
261-
<< decl->getName() << "\n");
262-
assert(!module.isVisibleExternally(decl));
263-
(void)decl;
280+
auto key = keyAndInfo.first;
281+
LLVM_DEBUG(llvm::dbgs()
282+
<< "Eliminating all formal access on " << getName(key) << "\n");
283+
assert(!isVisibleExternally(key, &module));
264284

265285
// Non-deterministic iteration, only used to set a flag.
266286
for (BeginAccessInst *beginAccess : info.beginAccessSet) {

test/SILOptimizer/access_storage_analysis.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,3 +723,25 @@ bb3(%7 : $Builtin.RawPointer):
723723
end_access %9 : $*Int64
724724
return %10 : $Int64
725725
}
726+
727+
// Test storage for SIL global variable declations.
728+
729+
sil_global hidden @testGlobal : $Builtin.Int64
730+
731+
sil hidden [global_init] @testAddressor : $@convention(thin) () -> Builtin.RawPointer {
732+
bb0:
733+
%4 = global_addr @testGlobal : $*Builtin.Int64
734+
%5 = address_to_pointer %4 : $*Builtin.Int64 to $Builtin.RawPointer
735+
return %5 : $Builtin.RawPointer
736+
}
737+
738+
sil hidden [transparent] @testGetter : $@convention(thin) () -> Builtin.Int64 {
739+
bb0:
740+
%2 = function_ref @testAddressor : $@convention(thin) () -> Builtin.RawPointer
741+
%3 = apply %2() : $@convention(thin) () -> Builtin.RawPointer
742+
%4 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*Builtin.Int64
743+
%5 = begin_access [read] [dynamic] %4 : $*Builtin.Int64
744+
%6 = load %5 : $*Builtin.Int64
745+
end_access %5 : $*Builtin.Int64
746+
return %6 : $Builtin.Int64
747+
}

0 commit comments

Comments
 (0)