Skip to content

Commit 025af31

Browse files
authored
[NFC] SILGlobalVariable utilities. (#16870)
* SILModule::isVisibleExternally utility for VarDecls. * Fix the SIL parser so it doesn't drop global variable decls. This information was getting lost in SIL printing/parsing. Some passes rely on it. Regardless of whether passes should rely on it, it is totally unacceptable for the SIL passes to have subtle differences in behavior depending on the frontend mode. So, if we don't want passes to rely on global variable decls, that needs to be enforced by the API independent of how the frontend is invoked or how SIL is serialized. * Use custom DemangleOptions to lookup global variable identifiers. * SILGlobalVariable utilities. Move the utilities that are required for recognizing SILGlobalVariable access into SILGlobalVariable.[h|cpp]. Structural SIL properties that are assumed by the optimizer, and thus required for SIL verification, should never be embedded in SILOptimizer passes, or even in SILOptimizer/Utils. Structural SIL properties need to be defined in /SIL. They are as much part of the SIL language as the opcode list. These particular utilities are required for working with SILGlobalVariables, and will be used by a whole-module access enforcement optimization. The primary API for recognizing SIL globals is `getVariableOfGlobalInit`. It is required to find the association between the addressor SILFunction marked [global_init], and the SILGlobalVariable being addressed. Other helper APIs expose more details about the addressor's SIL patterns and are useful for transforming the initializer itself into an optimized form.
1 parent 5308a2a commit 025af31

File tree

4 files changed

+167
-111
lines changed

4 files changed

+167
-111
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class AccessedStorage {
221221
}
222222
}
223223

224+
/// Return true if the storage is guaranteed local.
224225
bool isLocal() const {
225226
switch (getKind()) {
226227
case Box:

include/swift/SIL/SILGlobalVariable.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,38 @@ public ilist_default_traits<::swift::SILGlobalVariable> {
225225

226226
} // end llvm namespace
227227

228+
//===----------------------------------------------------------------------===//
229+
// Utilities for verification and optimization.
230+
//===----------------------------------------------------------------------===//
231+
232+
namespace swift {
233+
234+
/// Given an addressor, AddrF, return the global variable being addressed, or
235+
/// return nullptr if the addressor isn't a recognized pattern.
236+
SILGlobalVariable *getVariableOfGlobalInit(SILFunction *AddrF);
237+
238+
/// Return the callee of a once call.
239+
SILFunction *getCalleeOfOnceCall(BuiltinInst *BI);
240+
241+
/// Helper for getVariableOfGlobalInit(), so GlobalOpts can deeply inspect and
242+
/// rewrite the initialization pattern.
243+
///
244+
/// Given an addressor, AddrF, find the call to the global initializer if
245+
/// present, otherwise return null. If an initializer is returned, then
246+
/// `CallsToOnce` is initialized to the corresponding builtin "once" call.
247+
SILFunction *findInitializer(SILModule *Module, SILFunction *AddrF,
248+
BuiltinInst *&CallToOnce);
249+
250+
/// Helper for getVariableOfGlobalInit(), so GlobalOpts can deeply inspect and
251+
/// rewrite the initialization pattern.
252+
///
253+
/// Given a global initializer, InitFunc, return the GlobalVariable that it
254+
/// statically initializes or return nullptr if it isn't an obvious static
255+
/// initializer. If a global variable is returned, InitVal is initialized to the
256+
/// the instruction producing the global's initial value.
257+
SILGlobalVariable *getVariableOfStaticInitializer(
258+
SILFunction *InitFunc, SingleValueInstruction *&InitVal);
259+
260+
} // namespace swift
261+
228262
#endif

lib/SIL/SILGlobalVariable.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift/SIL/SILFunction.h"
1314
#include "swift/SIL/SILGlobalVariable.h"
15+
#include "swift/SIL/SILInstruction.h"
1416
#include "swift/SIL/SILLinkage.h"
1517
#include "swift/SIL/SILModule.h"
1618

@@ -137,3 +139,132 @@ ClangNode SILGlobalVariable::getClangNode() const {
137139
const clang::Decl *SILGlobalVariable::getClangDecl() const {
138140
return (VDecl ? VDecl->getClangDecl() : nullptr);
139141
}
142+
143+
//===----------------------------------------------------------------------===//
144+
// Utilities for verification and optimization.
145+
//===----------------------------------------------------------------------===//
146+
147+
static SILGlobalVariable *getStaticallyInitializedVariable(SILFunction *AddrF) {
148+
if (AddrF->isExternalDeclaration())
149+
return nullptr;
150+
151+
auto *RetInst = cast<ReturnInst>(AddrF->findReturnBB()->getTerminator());
152+
auto *API = dyn_cast<AddressToPointerInst>(RetInst->getOperand());
153+
if (!API)
154+
return nullptr;
155+
auto *GAI = dyn_cast<GlobalAddrInst>(API->getOperand());
156+
if (!GAI)
157+
return nullptr;
158+
159+
return GAI->getReferencedGlobal();
160+
}
161+
162+
SILGlobalVariable *swift::getVariableOfGlobalInit(SILFunction *AddrF) {
163+
if (!AddrF->isGlobalInit())
164+
return nullptr;
165+
166+
if (auto *SILG = getStaticallyInitializedVariable(AddrF))
167+
return SILG;
168+
169+
// If the addressor contains a single "once" call, it calls globalinit_func,
170+
// and the globalinit_func is called by "once" from a single location,
171+
// continue; otherwise bail.
172+
BuiltinInst *CallToOnce;
173+
auto *InitF = findInitializer(&AddrF->getModule(), AddrF, CallToOnce);
174+
175+
if (!InitF || !InitF->getName().startswith("globalinit_"))
176+
return nullptr;
177+
178+
// If the globalinit_func is trivial, continue; otherwise bail.
179+
SingleValueInstruction *dummyInitVal;
180+
auto *SILG = getVariableOfStaticInitializer(InitF, dummyInitVal);
181+
182+
return SILG;
183+
}
184+
185+
SILFunction *swift::getCalleeOfOnceCall(BuiltinInst *BI) {
186+
assert(BI->getNumOperands() == 2 && "once call should have 2 operands.");
187+
188+
auto Callee = BI->getOperand(1);
189+
assert(Callee->getType().castTo<SILFunctionType>()->getRepresentation()
190+
== SILFunctionTypeRepresentation::CFunctionPointer &&
191+
"Expected C function representation!");
192+
193+
if (auto *FR = dyn_cast<FunctionRefInst>(Callee))
194+
return FR->getReferencedFunction();
195+
196+
return nullptr;
197+
}
198+
199+
// Find the globalinit_func by analyzing the body of the addressor.
200+
SILFunction *swift::findInitializer(SILModule *Module, SILFunction *AddrF,
201+
BuiltinInst *&CallToOnce) {
202+
// We only handle a single SILBasicBlock for now.
203+
if (AddrF->size() != 1)
204+
return nullptr;
205+
206+
CallToOnce = nullptr;
207+
SILBasicBlock *BB = &AddrF->front();
208+
for (auto &I : *BB) {
209+
// Find the builtin "once" call.
210+
if (auto *BI = dyn_cast<BuiltinInst>(&I)) {
211+
const BuiltinInfo &Builtin =
212+
BI->getModule().getBuiltinInfo(BI->getName());
213+
if (Builtin.ID != BuiltinValueKind::Once)
214+
continue;
215+
216+
// Bail if we have multiple "once" calls in the addressor.
217+
if (CallToOnce)
218+
return nullptr;
219+
220+
CallToOnce = BI;
221+
}
222+
}
223+
if (!CallToOnce)
224+
return nullptr;
225+
return getCalleeOfOnceCall(CallToOnce);
226+
}
227+
228+
SILGlobalVariable *
229+
swift::getVariableOfStaticInitializer(SILFunction *InitFunc,
230+
SingleValueInstruction *&InitVal) {
231+
InitVal = nullptr;
232+
SILGlobalVariable *GVar = nullptr;
233+
// We only handle a single SILBasicBlock for now.
234+
if (InitFunc->size() != 1)
235+
return nullptr;
236+
237+
SILBasicBlock *BB = &InitFunc->front();
238+
GlobalAddrInst *SGA = nullptr;
239+
bool HasStore = false;
240+
for (auto &I : *BB) {
241+
// Make sure we have a single GlobalAddrInst and a single StoreInst.
242+
// And the StoreInst writes to the GlobalAddrInst.
243+
if (isa<AllocGlobalInst>(&I) || isa<ReturnInst>(&I)
244+
|| isa<DebugValueInst>(&I)) {
245+
continue;
246+
} else if (auto *sga = dyn_cast<GlobalAddrInst>(&I)) {
247+
if (SGA)
248+
return nullptr;
249+
SGA = sga;
250+
GVar = SGA->getReferencedGlobal();
251+
} else if (auto *SI = dyn_cast<StoreInst>(&I)) {
252+
if (HasStore || SI->getDest() != SGA)
253+
return nullptr;
254+
HasStore = true;
255+
SILValue value = SI->getSrc();
256+
257+
// We only handle StructInst and TupleInst being stored to a
258+
// global variable for now.
259+
if (!isa<StructInst>(value) && !isa<TupleInst>(value))
260+
return nullptr;
261+
InitVal = cast<SingleValueInstruction>(value);
262+
} else if (!SILGlobalVariable::isValidStaticInitializerInst(&I,
263+
I.getModule())) {
264+
return nullptr;
265+
}
266+
}
267+
if (!InitVal)
268+
return nullptr;
269+
return GVar;
270+
}

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/Demangling/Demangle.h"
1515
#include "swift/SIL/CFG.h"
1616
#include "swift/SIL/DebugUtils.h"
17+
#include "swift/SIL/SILGlobalVariable.h"
1718
#include "swift/SIL/SILInstruction.h"
1819
#include "swift/SILOptimizer/Analysis/ColdBlockInfo.h"
1920
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
@@ -113,8 +114,6 @@ class SILGlobalOpt {
113114
/// This is the main entrypoint for collecting global accesses.
114115
void collectGlobalAccess(GlobalAddrInst *GAI);
115116

116-
SILGlobalVariable *getVariableOfGlobalInit(SILFunction *AddrF);
117-
118117
/// Returns true if we think that \p CurBB is inside a loop.
119118
bool isInLoop(SILBasicBlock *CurBB);
120119

@@ -349,21 +348,6 @@ void SILGlobalOpt::collectGlobalStore(StoreInst *SI, SILGlobalVariable *SILG) {
349348
GlobalVarStore[SILG] = SI;
350349
}
351350

352-
/// Return the callee of a once call.
353-
static SILFunction *getCalleeOfOnceCall(BuiltinInst *BI) {
354-
assert(BI->getNumOperands() == 2 && "once call should have 2 operands.");
355-
356-
auto Callee = BI->getOperand(1);
357-
assert(Callee->getType().castTo<SILFunctionType>()->getRepresentation()
358-
== SILFunctionTypeRepresentation::CFunctionPointer &&
359-
"Expected C function representation!");
360-
361-
if (auto *FR = dyn_cast<FunctionRefInst>(Callee))
362-
return FR->getReferencedFunction();
363-
364-
return nullptr;
365-
}
366-
367351
// Update UnhandledOnceCallee and InitializerCount by going through all "once"
368352
// calls.
369353
void SILGlobalOpt::collectOnceCall(BuiltinInst *BI) {
@@ -596,34 +580,6 @@ static SILFunction *genGetterFromInit(SILFunction *InitF, VarDecl *varDecl) {
596580
return GetterF;
597581
}
598582

599-
/// Find the globalinit_func by analyzing the body of the addressor.
600-
static SILFunction *findInitializer(SILModule *Module, SILFunction *AddrF,
601-
BuiltinInst *&CallToOnce) {
602-
// We only handle a single SILBasicBlock for now.
603-
if (AddrF->size() != 1)
604-
return nullptr;
605-
606-
CallToOnce = nullptr;
607-
SILBasicBlock *BB = &AddrF->front();
608-
for (auto &I : *BB) {
609-
// Find the builtin "once" call.
610-
if (auto *BI = dyn_cast<BuiltinInst>(&I)) {
611-
const BuiltinInfo &Builtin = Module->getBuiltinInfo(BI->getName());
612-
if (Builtin.ID != BuiltinValueKind::Once)
613-
continue;
614-
615-
// Bail if we have multiple "once" calls in the addressor.
616-
if (CallToOnce)
617-
return nullptr;
618-
619-
CallToOnce = BI;
620-
}
621-
}
622-
if (!CallToOnce)
623-
return nullptr;
624-
return getCalleeOfOnceCall(CallToOnce);
625-
}
626-
627583
/// Checks if a given global variable is assigned only once.
628584
static bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) {
629585
if (SILG->isLet())
@@ -669,49 +625,6 @@ static SILValue convertLoadSequence(SILValue oldSequence,
669625
return nullptr;
670626
}
671627

672-
static SILGlobalVariable *getVariableOfStaticInitializer(SILFunction *InitFunc,
673-
SingleValueInstruction *&InitVal) {
674-
InitVal = nullptr;
675-
SILGlobalVariable *GVar = nullptr;
676-
// We only handle a single SILBasicBlock for now.
677-
if (InitFunc->size() != 1)
678-
return nullptr;
679-
680-
SILBasicBlock *BB = &InitFunc->front();
681-
GlobalAddrInst *SGA = nullptr;
682-
bool HasStore = false;
683-
for (auto &I : *BB) {
684-
// Make sure we have a single GlobalAddrInst and a single StoreInst.
685-
// And the StoreInst writes to the GlobalAddrInst.
686-
if (isa<AllocGlobalInst>(&I) || isa<ReturnInst>(&I)
687-
|| isa<DebugValueInst>(&I)) {
688-
continue;
689-
} else if (auto *sga = dyn_cast<GlobalAddrInst>(&I)) {
690-
if (SGA)
691-
return nullptr;
692-
SGA = sga;
693-
GVar = SGA->getReferencedGlobal();
694-
} else if (auto *SI = dyn_cast<StoreInst>(&I)) {
695-
if (HasStore || SI->getDest() != SGA)
696-
return nullptr;
697-
HasStore = true;
698-
SILValue value = SI->getSrc();
699-
700-
// We only handle StructInst and TupleInst being stored to a
701-
// global variable for now.
702-
if (!isa<StructInst>(value) && !isa<TupleInst>(value))
703-
return nullptr;
704-
InitVal = cast<SingleValueInstruction>(value);
705-
} else if (!SILGlobalVariable::isValidStaticInitializerInst(&I,
706-
I.getModule())) {
707-
return nullptr;
708-
}
709-
}
710-
if (!InitVal)
711-
return nullptr;
712-
return GVar;
713-
}
714-
715628
/// Replace loads from a global variable by the known value.
716629
void SILGlobalOpt::
717630
replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
@@ -822,29 +735,6 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
822735
HasChanged = true;
823736
}
824737

825-
SILGlobalVariable *SILGlobalOpt::getVariableOfGlobalInit(SILFunction *AddrF) {
826-
if (!AddrF->isGlobalInit())
827-
return nullptr;
828-
829-
// If the addressor contains a single "once" call, it calls globalinit_func,
830-
// and the globalinit_func is called by "once" from a single location,
831-
// continue; otherwise bail.
832-
BuiltinInst *CallToOnce;
833-
auto *InitF = findInitializer(Module, AddrF, CallToOnce);
834-
835-
if (!InitF || !InitF->getName().startswith("globalinit_")
836-
|| InitializerCount[InitF] > 1)
837-
return nullptr;
838-
839-
// If the globalinit_func is trivial, continue; otherwise bail.
840-
SingleValueInstruction *dummyInitVal;
841-
auto *SILG = getVariableOfStaticInitializer(InitF, dummyInitVal);
842-
if (!SILG || !SILG->isDefinition())
843-
return nullptr;
844-
845-
return SILG;
846-
}
847-
848738
static bool canBeChangedExternally(SILGlobalVariable *SILG) {
849739
// Don't assume anything about globals which are imported from other modules.
850740
if (isAvailableExternally(SILG->getLinkage()))

0 commit comments

Comments
 (0)