Skip to content

Commit 3b9014d

Browse files
authored
Merge pull request #28324 from zoecarver/optimize/dead-global-vars
Optimize dead private global variables
2 parents ecdaf17 + 1f66767 commit 3b9014d

File tree

5 files changed

+319
-21
lines changed

5 files changed

+319
-21
lines changed

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 238 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SIL/SILCloner.h"
2121
#include "swift/SIL/SILGlobalVariable.h"
2222
#include "swift/SIL/SILInstruction.h"
23+
#include "swift/SIL/SILInstructionWorklist.h"
2324
#include "swift/SILOptimizer/Analysis/ColdBlockInfo.h"
2425
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
2526
#include "swift/SILOptimizer/PassManager/Passes.h"
@@ -59,6 +60,8 @@ class SILGlobalOpt {
5960

6061
typedef SmallVector<ApplyInst *, 4> GlobalInitCalls;
6162
typedef SmallVector<LoadInst *, 4> GlobalLoads;
63+
typedef SmallVector<BeginAccessInst *, 4> GlobalAccesses;
64+
typedef SmallVector<GlobalAddrInst *, 4> GlobalAddrs;
6265

6366
/// A map from each visited global initializer call to a list of call sites.
6467
llvm::MapVector<SILFunction *, GlobalInitCalls> GlobalInitCallMap;
@@ -70,10 +73,20 @@ class SILGlobalOpt {
7073
/// A map from each visited global let variable to its set of loads.
7174
llvm::MapVector<SILGlobalVariable *, GlobalLoads> GlobalLoadMap;
7275

76+
/// A map from each visited global to its set of begin_access instructions.
77+
llvm::MapVector<SILGlobalVariable *, GlobalAccesses> GlobalAccessMap;
78+
79+
/// A map from each visited global to all of its global address instructions.
80+
llvm::MapVector<SILGlobalVariable *, GlobalAddrs> GlobalAddrMap;
81+
7382
/// A map from each visited global let variable to the store instructions
7483
/// which initialize it.
7584
llvm::MapVector<SILGlobalVariable *, StoreInst *> GlobalVarStore;
7685

86+
/// A map for each visited global variable to the alloc instruction that
87+
/// allocated space for it.
88+
llvm::MapVector<SILGlobalVariable *, AllocGlobalInst *> AllocGlobalStore;
89+
7790
/// A set of visited global variables that for some reason we have decided is
7891
/// not able to be optimized safely or for which we do not know how to
7992
/// optimize safely.
@@ -97,13 +110,25 @@ class SILGlobalOpt {
97110
/// A map from a globalinit_func to the number of times "once" has called the
98111
/// function.
99112
llvm::DenseMap<SILFunction *, unsigned> InitializerCount;
113+
114+
llvm::SmallVector<SILInstruction *, 4> InstToRemove;
115+
llvm::SmallVector<SILGlobalVariable *, 4> GlobalsToRemove;
116+
100117
public:
101118
SILGlobalOpt(SILOptFunctionBuilder &FunctionBuilder, SILModule *M, DominanceAnalysis *DA)
102119
: FunctionBuilder(FunctionBuilder), Module(M), DA(DA) {}
103120

104121
bool run();
105122

106123
protected:
124+
/// Reset all the maps of global variables.
125+
void reset();
126+
127+
/// Collect all global variables.
128+
void collect();
129+
130+
void collectUsesOfInstructionForDeletion(SILInstruction *inst);
131+
107132
/// If this is a call to a global initializer, map it.
108133
void collectGlobalInitCall(ApplyInst *AI);
109134

@@ -118,6 +143,11 @@ class SILGlobalOpt {
118143
/// This is the main entrypoint for collecting global accesses.
119144
void collectGlobalAccess(GlobalAddrInst *GAI);
120145

146+
/// Simple function to collect globals and their corresponding alloc
147+
/// instructions.
148+
void collectAllocGlobal(SILGlobalVariable *global,
149+
AllocGlobalInst *allocGlobal);
150+
121151
/// Returns true if we think that \p CurBB is inside a loop.
122152
bool isInLoop(SILBasicBlock *CurBB);
123153

@@ -139,6 +169,17 @@ class SILGlobalOpt {
139169
/// can be statically initialized.
140170
void optimizeInitializer(SILFunction *AddrF, GlobalInitCalls &Calls);
141171

172+
/// If possible, remove global address instructions associated with the given
173+
/// global.
174+
bool tryRemoveGlobalAddr(SILGlobalVariable *global);
175+
176+
/// If possible, remove global alloc instructions associated with the given
177+
/// global.
178+
bool tryRemoveGlobalAlloc(SILGlobalVariable *global, AllocGlobalInst *alloc);
179+
180+
/// If a global has no uses, remove it.
181+
bool tryRemoveUnusedGlobal(SILGlobalVariable *global);
182+
142183
/// Optimize access to the global variable, which is known to have a constant
143184
/// value. Replace all loads from the global address by invocations of a
144185
/// getter that returns the value of this variable.
@@ -809,6 +850,110 @@ static bool canBeChangedExternally(SILGlobalVariable *SILG) {
809850
return true;
810851
}
811852

853+
static bool canBeUsedOrChangedExternally(SILGlobalVariable *global) {
854+
if (global->isLet())
855+
return isPossiblyUsedExternally(global->getLinkage(),
856+
global->getModule().isWholeModule());
857+
return canBeChangedExternally(global);
858+
}
859+
860+
static bool isSafeToRemove(SILGlobalVariable *global) {
861+
return global->getDecl() && !canBeUsedOrChangedExternally(global);
862+
}
863+
864+
bool SILGlobalOpt::tryRemoveGlobalAlloc(SILGlobalVariable *global,
865+
AllocGlobalInst *alloc) {
866+
if (!isSafeToRemove(global))
867+
return false;
868+
869+
// Make sure the global's address is never taken and we shouldn't skip this
870+
// global.
871+
if (GlobalVarSkipProcessing.count(global) ||
872+
(GlobalAddrMap[global].size() &&
873+
std::any_of(GlobalAddrMap[global].begin(), GlobalAddrMap[global].end(),
874+
[=](GlobalAddrInst *addr) {
875+
return std::find(InstToRemove.begin(), InstToRemove.end(),
876+
addr) == InstToRemove.end();
877+
})))
878+
return false;
879+
880+
InstToRemove.push_back(alloc);
881+
return true;
882+
}
883+
884+
/// If there are no loads or accesses of a given global, then remove its
885+
/// associated global addr and all asssociated instructions.
886+
bool SILGlobalOpt::tryRemoveGlobalAddr(SILGlobalVariable *global) {
887+
if (!isSafeToRemove(global))
888+
return false;
889+
890+
if (GlobalVarSkipProcessing.count(global) || GlobalLoadMap[global].size() ||
891+
GlobalAccessMap[global].size())
892+
return false;
893+
894+
// Check if the address is used in anything but a store. If any global_addr
895+
// instruction associated with a global is used in anything but a store, we
896+
// can't remove ANY global_addr instruction associated with that global.
897+
for (auto *addr : GlobalAddrMap[global]) {
898+
for (auto *use : addr->getUses()) {
899+
if (!isa<StoreInst>(use->getUser()))
900+
return false;
901+
}
902+
}
903+
904+
// Now that it's safe, remove all global addresses associated with this global
905+
for (auto *addr : GlobalAddrMap[global]) {
906+
InstToRemove.push_back(addr);
907+
}
908+
909+
return true;
910+
}
911+
912+
bool SILGlobalOpt::tryRemoveUnusedGlobal(SILGlobalVariable *global) {
913+
if (!isSafeToRemove(global))
914+
return false;
915+
916+
if (GlobalVarSkipProcessing.count(global))
917+
return false;
918+
919+
// If this global is used, check if the user is going to be removed.
920+
// Make sure none of the removed instructions are the same as this global's
921+
// alloc instruction
922+
if (AllocGlobalStore.count(global) &&
923+
std::none_of(InstToRemove.begin(), InstToRemove.end(),
924+
[=](SILInstruction *inst) {
925+
return AllocGlobalStore[global] == inst;
926+
}))
927+
return false;
928+
929+
if (GlobalVarStore.count(global) &&
930+
std::none_of(
931+
InstToRemove.begin(), InstToRemove.end(),
932+
[=](SILInstruction *inst) { return GlobalVarStore[global] == inst; }))
933+
return false;
934+
935+
// Check if any of the global_addr instructions associated with this global
936+
// aren't going to be removed. In that case, we need to keep the global.
937+
if (GlobalAddrMap[global].size() &&
938+
std::any_of(GlobalAddrMap[global].begin(), GlobalAddrMap[global].end(),
939+
[=](GlobalAddrInst *addr) {
940+
return std::find(InstToRemove.begin(), InstToRemove.end(),
941+
addr) == InstToRemove.end();
942+
}))
943+
return false;
944+
945+
if (GlobalAccessMap[global].size() &&
946+
std::any_of(GlobalAccessMap[global].begin(),
947+
GlobalAccessMap[global].end(), [=](BeginAccessInst *access) {
948+
return std::find(InstToRemove.begin(), InstToRemove.end(),
949+
access) == InstToRemove.end();
950+
}))
951+
return false;
952+
953+
GlobalsToRemove.push_back(global);
954+
return true;
955+
}
956+
812957
/// Check if instruction I is a load from instruction V or
813958
/// or a struct_element_addr from instruction V.
814959
/// returns instruction I if this condition holds, or nullptr otherwise.
@@ -837,6 +982,11 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
837982
if (!SILG)
838983
return;
839984

985+
if (!SILG->getDecl())
986+
return;
987+
988+
GlobalAddrMap[SILG].push_back(GAI);
989+
840990
if (!SILG->isLet()) {
841991
// We cannot determine the value for global variables which could be
842992
// changed externally at run-time.
@@ -861,9 +1011,6 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
8611011
if (GlobalVar == SILG)
8621012
return;
8631013

864-
if (!SILG->getDecl())
865-
return;
866-
8671014
for (auto *Op : getNonDebugUses(GAI)) {
8681015
if (auto *SI = dyn_cast<StoreInst>(Op->getUser())) {
8691016
if (SI->getDest() == GAI)
@@ -876,6 +1023,10 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
8761023
continue;
8771024
}
8781025

1026+
if (auto *beginAccess = dyn_cast<BeginAccessInst>(Op->getUser())) {
1027+
GlobalAccessMap[SILG].push_back(beginAccess);
1028+
}
1029+
8791030
LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: has non-store, non-load use: "
8801031
<< SILG->getName() << '\n';
8811032
Op->getUser()->dump());
@@ -886,6 +1037,11 @@ void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
8861037
}
8871038
}
8881039

1040+
void SILGlobalOpt::collectAllocGlobal(SILGlobalVariable *global,
1041+
AllocGlobalInst *allocGlobal) {
1042+
AllocGlobalStore[global] = allocGlobal;
1043+
}
1044+
8891045
// Optimize access to the global variable, which is known to have a constant
8901046
// value. Replace all loads from the global address by invocations of a getter
8911047
// that returns the value of this variable.
@@ -906,7 +1062,7 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG,
9061062
return;
9071063
}
9081064

909-
if (!GlobalLoadMap.count(SILG)) {
1065+
if (GlobalLoadMap[SILG].empty()) {
9101066
LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: not in load map: "
9111067
<< SILG->getName() << '\n');
9121068
return;
@@ -938,13 +1094,17 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG,
9381094

9391095
}
9401096

941-
bool SILGlobalOpt::run() {
942-
for (auto &F : *Module) {
943-
944-
// Don't optimize functions that are marked with the opt.never attribute.
945-
if (!F.shouldOptimize())
946-
continue;
1097+
void SILGlobalOpt::reset() {
1098+
AllocGlobalStore.clear();
1099+
GlobalVarStore.clear();
1100+
GlobalAddrMap.clear();
1101+
GlobalAccessMap.clear();
1102+
GlobalLoadMap.clear();
1103+
GlobalInitCallMap.clear();
1104+
}
9471105

1106+
void SILGlobalOpt::collect() {
1107+
for (auto &F : *Module) {
9481108
// TODO: Add support for ownership.
9491109
if (F.hasOwnership()) {
9501110
continue;
@@ -967,27 +1127,92 @@ bool SILGlobalOpt::run() {
9671127
continue;
9681128
}
9691129

970-
auto *GAI = dyn_cast<GlobalAddrInst>(&I);
971-
if (!GAI) {
1130+
if (auto *GAI = dyn_cast<GlobalAddrInst>(&I)) {
1131+
collectGlobalAccess(GAI);
9721132
continue;
9731133
}
9741134

975-
collectGlobalAccess(GAI);
1135+
if (auto *allocGlobal = dyn_cast<AllocGlobalInst>(&I)) {
1136+
collectAllocGlobal(allocGlobal->getReferencedGlobal(), allocGlobal);
1137+
continue;
1138+
}
9761139
}
9771140
}
9781141
}
1142+
}
1143+
1144+
bool SILGlobalOpt::run() {
1145+
// Collect all the global variables and associated instructions.
1146+
collect();
9791147

1148+
// Optimize based on what we just collected.
9801149
for (auto &InitCalls : GlobalInitCallMap) {
1150+
// Don't optimize functions that are marked with the opt.never attribute.
1151+
if (!InitCalls.first->shouldOptimize())
1152+
continue;
1153+
9811154
// Optimize the addressors if possible.
9821155
optimizeInitializer(InitCalls.first, InitCalls.second);
9831156
placeInitializers(InitCalls.first, InitCalls.second);
9841157
}
9851158

9861159
for (auto &Init : GlobalVarStore) {
1160+
// Don't optimize functions that are marked with the opt.never attribute.
1161+
if (!Init.second->getFunction()->shouldOptimize())
1162+
continue;
1163+
9871164
// Optimize the access to globals if possible.
9881165
optimizeGlobalAccess(Init.first, Init.second);
9891166
}
9901167

1168+
SmallVector<SILGlobalVariable *, 8> addrGlobals;
1169+
for (auto &addrPair : GlobalAddrMap) {
1170+
// Don't optimize functions that are marked with the opt.never attribute.
1171+
bool shouldOptimize = true;
1172+
for (auto *addr : addrPair.second) {
1173+
if (!addr->getFunction()->shouldOptimize()) {
1174+
shouldOptimize = false;
1175+
break;
1176+
}
1177+
}
1178+
if (!shouldOptimize)
1179+
continue;
1180+
1181+
addrGlobals.push_back(addrPair.first);
1182+
}
1183+
1184+
for (auto *global : addrGlobals) {
1185+
HasChanged |= tryRemoveGlobalAddr(global);
1186+
}
1187+
1188+
SmallVector<std::pair<SILGlobalVariable *, AllocGlobalInst *>, 12>
1189+
globalAllocPairs;
1190+
for (auto &alloc : AllocGlobalStore) {
1191+
if (!alloc.second->getFunction()->shouldOptimize())
1192+
continue;
1193+
globalAllocPairs.push_back(std::make_pair(alloc.first, alloc.second));
1194+
}
1195+
1196+
for (auto &allocPair : globalAllocPairs) {
1197+
HasChanged |= tryRemoveGlobalAlloc(allocPair.first, allocPair.second);
1198+
}
1199+
1200+
// Erase the instructions that we have marked for deletion.
1201+
for (auto *inst : InstToRemove) {
1202+
eraseUsesOfInstruction(inst);
1203+
inst->eraseFromParent();
1204+
}
1205+
1206+
for (auto &global : Module->getSILGlobals()) {
1207+
HasChanged |= tryRemoveUnusedGlobal(&global);
1208+
}
1209+
1210+
for (auto *global : GlobalsToRemove) {
1211+
Module->eraseGlobalVariable(global);
1212+
}
1213+
1214+
// Reset in case we re-run this function (when HasChanged is true).
1215+
reset();
9911216
return HasChanged;
9921217
}
9931218

0 commit comments

Comments
 (0)