Skip to content

Commit 0873622

Browse files
authored
Merge pull request #30445 from eeckstein/globalopt
GlobalOpt: don't speculatively execute initializers of global variables
2 parents d8de5ba + 3ad7d54 commit 0873622

File tree

10 files changed

+453
-636
lines changed

10 files changed

+453
-636
lines changed

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 29 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ class SILGlobalOpt {
146146
SILFunction *ParentF,
147147
llvm::DenseMap<SILFunction *, ApplyInst *> &ParentFuncs);
148148

149-
void placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst *> Calls);
150-
151149
/// Update UnhandledOnceCallee and InitializerCount by going through all
152150
/// "once" calls.
153151
void collectOnceCall(BuiltinInst *AI);
@@ -275,6 +273,25 @@ void SILGlobalOpt::collectOnceCall(BuiltinInst *BI) {
275273
InitializerCount[Callee]++;
276274
}
277275

276+
static bool isPotentialStore(SILInstruction *inst) {
277+
switch (inst->getKind()) {
278+
case SILInstructionKind::LoadInst:
279+
return false;
280+
case SILInstructionKind::PointerToAddressInst:
281+
case SILInstructionKind::StructElementAddrInst:
282+
case SILInstructionKind::TupleElementAddrInst:
283+
for (Operand *op : cast<SingleValueInstruction>(inst)->getUses()) {
284+
if (isPotentialStore(op->getUser()))
285+
return true;
286+
}
287+
return false;
288+
case SILInstructionKind::BeginAccessInst:
289+
return cast<BeginAccessInst>(inst)->getAccessKind() != SILAccessKind::Read;
290+
default:
291+
return true;
292+
}
293+
}
294+
278295
/// return true if this block is inside a loop.
279296
bool SILGlobalOpt::isInLoop(SILBasicBlock *CurBB) {
280297
SILFunction *F = CurBB->getParent();
@@ -292,164 +309,25 @@ bool SILGlobalOpt::isInLoop(SILBasicBlock *CurBB) {
292309
return LoopBlocks.count(CurBB);
293310
}
294311

295-
/// Returns true if the block \p BB is terminated with a cond_br based on an
296-
/// availability check.
297-
static bool isAvailabilityCheck(SILBasicBlock *BB) {
298-
auto *CBR = dyn_cast<CondBranchInst>(BB->getTerminator());
299-
if (!CBR)
300-
return false;
301-
302-
auto *AI = dyn_cast<ApplyInst>(CBR->getCondition());
303-
if (!AI)
304-
return false;
305-
306-
SILFunction *F = AI->getReferencedFunctionOrNull();
307-
if (!F || !F->hasSemanticsAttrs())
308-
return false;
309-
310-
return F->hasSemanticsAttrThatStartsWith("availability");
311-
}
312-
313-
/// Returns true if there are any availability checks along the dominator tree
314-
/// from \p From to \p To.
315-
static bool isAvailabilityCheckOnDomPath(SILBasicBlock *From, SILBasicBlock *To,
316-
DominanceInfo *DT) {
317-
if (From == To)
318-
return false;
319-
320-
auto *Node = DT->getNode(To)->getIDom();
321-
for (;;) {
322-
SILBasicBlock *BB = Node->getBlock();
323-
if (isAvailabilityCheck(BB))
324-
return true;
325-
if (BB == From)
326-
return false;
327-
Node = Node->getIDom();
328-
assert(Node && "Should have hit To-block");
329-
}
330-
}
331-
332-
ApplyInst *SILGlobalOpt::getHoistedApplyForInitializer(
333-
ApplyInst *AI, DominanceInfo *DT, SILFunction *InitF, SILFunction *ParentF,
334-
llvm::DenseMap<SILFunction *, ApplyInst *> &ParentFuncs) {
335-
auto PFI = ParentFuncs.find(ParentF);
336-
if (PFI == ParentFuncs.end()) {
337-
ParentFuncs[ParentF] = AI;
338-
339-
// It's the first time we found a call to InitF in this function, so we
340-
// try to hoist it out of any loop.
341-
return AI;
342-
}
343-
344-
// Found a replacement for this init call. Ensure the replacement dominates
345-
// the original call site.
346-
ApplyInst *CommonAI = PFI->second;
347-
assert(cast<FunctionRefInst>(CommonAI->getCallee())
348-
->getReferencedFunctionOrNull() == InitF &&
349-
"ill-formed global init call");
350-
SILBasicBlock *DomBB =
351-
DT->findNearestCommonDominator(AI->getParent(), CommonAI->getParent());
352-
353-
// We must not move initializers around availability-checks.
354-
if (isAvailabilityCheckOnDomPath(DomBB, CommonAI->getParent(), DT))
355-
return nullptr;
356-
357-
ApplyInst *Result = nullptr;
358-
if (DomBB != CommonAI->getParent()) {
359-
CommonAI->moveBefore(&*DomBB->begin());
360-
placeFuncRef(CommonAI, DT);
361-
362-
// Try to hoist the existing AI again if we move it to another block,
363-
// e.g. from a loop exit into the loop.
364-
Result = CommonAI;
365-
}
366-
367-
AI->replaceAllUsesWith(CommonAI);
368-
AI->eraseFromParent();
369-
HasChanged = true;
370-
return Result;
371-
}
372-
373-
/// Optimize placement of initializer calls given a list of calls to the
374-
/// same initializer. All original initialization points must be dominated by
375-
/// the final initialization calls.
376-
///
377-
/// The current heuristic hoists all initialization points within a function to
378-
/// a single dominating call in the outer loop preheader.
379-
void SILGlobalOpt::placeInitializers(SILFunction *InitF,
380-
ArrayRef<ApplyInst *> Calls) {
381-
LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: calls to "
382-
<< Demangle::demangleSymbolAsString(InitF->getName())
383-
<< " : " << Calls.size() << "\n");
384-
// Map each initializer-containing function to its final initializer call.
385-
llvm::DenseMap<SILFunction *, ApplyInst *> ParentFuncs;
386-
for (auto *AI : Calls) {
387-
assert(AI->getNumArguments() == 0 && "ill-formed global init call");
388-
assert(
389-
cast<FunctionRefInst>(AI->getCallee())->getReferencedFunctionOrNull() ==
390-
InitF &&
391-
"wrong init call");
392-
SILFunction *ParentF = AI->getFunction();
393-
DominanceInfo *DT = DA->get(ParentF);
394-
ApplyInst *HoistAI =
395-
getHoistedApplyForInitializer(AI, DT, InitF, ParentF, ParentFuncs);
396-
397-
// If we were unable to find anything, just go onto the next apply.
398-
if (!HoistAI) {
399-
continue;
400-
}
401-
402-
// Otherwise, move this call to the outermost loop preheader.
403-
SILBasicBlock *BB = HoistAI->getParent();
404-
typedef llvm::DomTreeNodeBase<SILBasicBlock> DomTreeNode;
405-
DomTreeNode *Node = DT->getNode(BB);
406-
while (Node) {
407-
SILBasicBlock *DomParentBB = Node->getBlock();
408-
if (isAvailabilityCheck(DomParentBB)) {
409-
LLVM_DEBUG(llvm::dbgs() << " don't hoist above availability check "
410-
"at bb"
411-
<< DomParentBB->getDebugID() << "\n");
412-
break;
413-
}
414-
BB = DomParentBB;
415-
if (!isInLoop(BB))
416-
break;
417-
Node = Node->getIDom();
418-
}
419-
420-
if (BB == HoistAI->getParent()) {
421-
// BB is either unreachable or not in a loop.
422-
LLVM_DEBUG(llvm::dbgs() << " skipping (not in a loop): " << *HoistAI
423-
<< " in " << HoistAI->getFunction()->getName()
424-
<< "\n");
425-
continue;
426-
}
427-
428-
LLVM_DEBUG(llvm::dbgs() << " hoisting: " << *HoistAI << " in "
429-
<< HoistAI->getFunction()->getName() << "\n");
430-
HoistAI->moveBefore(&*BB->begin());
431-
placeFuncRef(HoistAI, DT);
432-
HasChanged = true;
433-
}
434-
}
435-
436312
bool SILGlobalOpt::isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG,
437313
SILFunction *globalAddrF) {
438314
if (SILG->isLet())
439315
return true;
440316

441317
// If we should skip this, it is probably because there are multiple stores.
442318
// Return false if there are multiple stores or no stores.
443-
if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG) ||
444-
// Check if there is more than one use the global addr function. If there
445-
// is only one use, it must be the use that we are trying to optimize, so
446-
// that is OK. If there is more than one use, one of the other uses may
447-
// have a store attached to it which means there may be more than one
448-
// assignment, so return false.
449-
(GlobalInitCallMap.count(globalAddrF) &&
450-
GlobalInitCallMap[globalAddrF].size() != 1))
319+
if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG))
451320
return false;
452321

322+
if (GlobalInitCallMap.count(globalAddrF)) {
323+
for (ApplyInst *initCall : GlobalInitCallMap[globalAddrF]) {
324+
for (auto *Op : getNonDebugUses(initCall)) {
325+
if (isPotentialStore(Op->getUser()))
326+
return false;
327+
}
328+
}
329+
}
330+
453331
// Otherwise, return true if this can't be used externally (false, otherwise).
454332
return !isPossiblyUsedExternally(SILG->getLinkage(),
455333
SILG->getModule().isWholeModule());
@@ -727,24 +605,6 @@ bool SILGlobalOpt::tryRemoveUnusedGlobal(SILGlobalVariable *global) {
727605
return true;
728606
}
729607

730-
static bool isPotentialStore(SILInstruction *inst) {
731-
switch (inst->getKind()) {
732-
case SILInstructionKind::LoadInst:
733-
case SILInstructionKind::EndAccessInst:
734-
return false;
735-
case SILInstructionKind::StructElementAddrInst:
736-
case SILInstructionKind::TupleElementAddrInst:
737-
case SILInstructionKind::BeginAccessInst:
738-
for (Operand *op : cast<SingleValueInstruction>(inst)->getUses()) {
739-
if (isPotentialStore(op->getUser()))
740-
return true;
741-
}
742-
return false;
743-
default:
744-
return true;
745-
}
746-
}
747-
748608
/// If this is a read from a global let variable, map it.
749609
void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
750610
auto *SILG = GAI->getReferencedGlobal();
@@ -921,10 +781,6 @@ bool SILGlobalOpt::run() {
921781
}
922782
} while (changed);
923783

924-
for (auto &InitCalls : GlobalInitCallMap) {
925-
placeInitializers(InitCalls.first, InitCalls.second);
926-
}
927-
928784
// This is similiar to optimizeInitializer, but it's for globals which are
929785
// initialized in the "main" function and not by an initializer function.
930786
for (auto &Init : GlobalVarStore) {

0 commit comments

Comments
 (0)