Skip to content

Commit afedd6d

Browse files
committed
copy required stuff from SideEffectAnalysis to AccessStorageAnalysis
... before we delete SideEffectAnalysis at all.
1 parent beb46eb commit afedd6d

File tree

2 files changed

+273
-20
lines changed

2 files changed

+273
-20
lines changed

include/swift/SILOptimizer/Analysis/AccessStorageAnalysis.h

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,19 @@
1919
// FunctionAccessStorage.unidentifiedAccess. This does not imply that all
2020
// accesses within the function have Unidentified AccessStorage.
2121
//
22-
// Note: This interprocedural analysis can be easily augmented to simultaneously
23-
// compute FunctionSideEffects, without using a separate analysis, by adding
24-
// FunctionSideEffects as a member of FunctionAccessStorage. However, passes
25-
// that use AccessStorageAnalysis do not currently need SideEffectAnalysis.
26-
//
2722
//===----------------------------------------------------------------------===//
2823
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H
2924
#define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESSED_STORAGE_ANALYSIS_H
3025

3126
#include "swift/SIL/MemAccessUtils.h"
3227
#include "swift/SIL/SILFunction.h"
3328
#include "swift/SIL/SILInstruction.h"
34-
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
29+
#include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h"
3530

3631
namespace swift {
3732

33+
class BasicCalleeAnalysis;
34+
3835
/// Information about a formal access within a function pertaining to a
3936
/// particular AccessStorage location.
4037
class StorageAccessInfo : public AccessStorage {
@@ -340,16 +337,121 @@ class FunctionAccessStorage {
340337
/// Use the GenericFunctionEffectAnalysis API to get the results of the analysis:
341338
/// - geEffects(SILFunction*)
342339
/// - getCallSiteEffects(FunctionEffects &callEffects, FullApplySite fullApply)
343-
class AccessStorageAnalysis
344-
: public GenericFunctionEffectAnalysis<FunctionAccessStorage> {
340+
class AccessStorageAnalysis : public BottomUpIPAnalysis {
341+
342+
/// Stores the analysis data, e.g. side-effects, for a function.
343+
struct FunctionInfo : public FunctionInfoBase<FunctionInfo> {
344+
345+
/// The function effects.
346+
FunctionAccessStorage functionEffects;
347+
348+
/// Back-link to the function.
349+
SILFunction *F;
350+
351+
/// Used during recomputation to indicate if the side-effects of a caller
352+
/// must be updated.
353+
bool needUpdateCallers = false;
354+
355+
FunctionInfo(SILFunction *F) : F(F) {}
356+
357+
/// Clears the analysis data on invalidation.
358+
void clear() { functionEffects.clear(); }
359+
};
360+
361+
typedef BottomUpFunctionOrder<FunctionInfo> FunctionOrder;
362+
363+
enum {
364+
/// The maximum call-graph recursion depth for recomputing the analysis.
365+
/// This is a relatively small number to reduce compile time in case of
366+
/// large cycles in the call-graph.
367+
/// In case of no cycles, we should not hit this limit at all because the
368+
/// pass manager processes functions in bottom-up order.
369+
MaxRecursionDepth = 5
370+
};
371+
372+
/// All the function effect information for the whole module.
373+
llvm::DenseMap<SILFunction *, FunctionInfo *> functionInfoMap;
374+
375+
/// The allocator for the map of values in FunctionInfoMap.
376+
llvm::SpecificBumpPtrAllocator<FunctionInfo> allocator;
377+
378+
/// Callee analysis, used for determining the callees at call sites.
379+
BasicCalleeAnalysis *BCA;
380+
345381
public:
346382
AccessStorageAnalysis()
347-
: GenericFunctionEffectAnalysis<FunctionAccessStorage>(
348-
SILAnalysisKind::AccessStorage) {}
383+
: BottomUpIPAnalysis(SILAnalysisKind::AccessStorage) {}
349384

350385
static bool classof(const SILAnalysis *S) {
351386
return S->getKind() == SILAnalysisKind::AccessStorage;
352387
}
388+
389+
const FunctionAccessStorage &getEffects(SILFunction *F) {
390+
FunctionInfo *functionInfo = getFunctionInfo(F);
391+
if (!functionInfo->isValid())
392+
recompute(functionInfo);
393+
return functionInfo->functionEffects;
394+
}
395+
396+
/// Get the merged effects of all callees at the given call site from the
397+
/// callee's perspective (don't transform parameter effects).
398+
void getCalleeEffects(FunctionAccessStorage &calleeEffects,
399+
FullApplySite fullApply);
400+
401+
/// Get the merge effects of all callees at the given call site from the
402+
/// caller's perspective. Parameter effects are translated into information
403+
/// for the caller's arguments, and local effects are dropped.
404+
void getCallSiteEffects(FunctionAccessStorage &callEffects,
405+
FullApplySite fullApply) {
406+
FunctionAccessStorage calleeEffects;
407+
getCalleeEffects(calleeEffects, fullApply);
408+
callEffects.mergeFromApply(calleeEffects, fullApply);
409+
}
410+
411+
BasicCalleeAnalysis *getBasicCalleeAnalysis() { return BCA; }
412+
413+
virtual void initialize(SILPassManager *PM) override;
414+
415+
/// Invalidate all information in this analysis.
416+
virtual void invalidate() override;
417+
418+
/// Invalidate all of the information for a specific function.
419+
virtual void invalidate(SILFunction *F, InvalidationKind K) override;
420+
421+
/// Notify the analysis about a newly created function.
422+
virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {}
423+
424+
/// Notify the analysis about a function which will be deleted from the
425+
/// module.
426+
virtual void notifyWillDeleteFunction(SILFunction *F) override {
427+
invalidate(F, InvalidationKind::Nothing);
428+
}
429+
430+
/// Notify the analysis about changed witness or vtables.
431+
virtual void invalidateFunctionTables() override {}
432+
433+
private:
434+
/// Gets or creates FunctionAccessStorage for \p F.
435+
FunctionInfo *getFunctionInfo(SILFunction *F) {
436+
FunctionInfo *&functionInfo = functionInfoMap[F];
437+
if (!functionInfo) {
438+
functionInfo = new (allocator.Allocate()) FunctionInfo(F);
439+
}
440+
return functionInfo;
441+
}
442+
443+
/// Analyze the side-effects of a function, including called functions.
444+
/// Visited callees are added to \p BottomUpOrder until \p RecursionDepth
445+
/// reaches MaxRecursionDepth.
446+
void analyzeFunction(FunctionInfo *functionInfo, FunctionOrder &bottomUpOrder,
447+
int recursionDepth);
448+
449+
void analyzeCall(FunctionInfo *functionInfo, FullApplySite fullApply,
450+
FunctionOrder &bottomUpOrder, int recursionDepth);
451+
452+
/// Recomputes the side-effect information for the function \p Initial and
453+
/// all called functions, up to a recursion depth of MaxRecursionDepth.
454+
void recompute(FunctionInfo *initialInfo);
353455
};
354456

355457
} // end namespace swift

lib/SILOptimizer/Analysis/AccessStorageAnalysis.cpp

Lines changed: 161 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -357,23 +357,19 @@ bool FunctionAccessStorage::summarizeFunction(SILFunction *F) {
357357
// conservative value, since analyzeInstruction will never be called.
358358
//
359359
// If FunctionSideEffects can be summarized, use that information.
360-
FunctionSideEffects functionSideEffects;
361-
if (!functionSideEffects.summarizeFunction(F)) {
360+
361+
auto b = F->getMemoryBehavior(/*observeRetains*/ false);
362+
if (b == SILInstruction::MemoryBehavior::MayHaveSideEffects) {
362363
setWorstEffects();
363364
// May as well consider this a successful summary since there are no
364365
// instructions to visit anyway.
365366
return true;
366367
}
367-
bool mayRead = functionSideEffects.getGlobalEffects().mayRead();
368-
bool mayWrite = functionSideEffects.getGlobalEffects().mayWrite();
369-
for (auto &paramEffects : functionSideEffects.getParameterEffects()) {
370-
mayRead |= paramEffects.mayRead();
371-
mayWrite |= paramEffects.mayWrite();
372-
}
373-
if (mayWrite)
368+
if (b >= SILInstruction::MemoryBehavior::MayWrite) {
374369
accessResult.setUnidentifiedAccess(SILAccessKind::Modify);
375-
else if (mayRead)
370+
} else if (b == SILInstruction::MemoryBehavior::MayRead) {
376371
accessResult.setUnidentifiedAccess(SILAccessKind::Read);
372+
}
377373

378374
// If function side effects is "readnone" then this result will have an empty
379375
// storageAccessSet and unidentifiedAccess == None.
@@ -393,6 +389,161 @@ bool FunctionAccessStorage::summarizeCall(FullApplySite fullApply) {
393389
return false;
394390
}
395391

392+
void AccessStorageAnalysis::initialize(
393+
SILPassManager *PM) {
394+
BCA = PM->getAnalysis<BasicCalleeAnalysis>();
395+
}
396+
397+
void AccessStorageAnalysis::invalidate() {
398+
functionInfoMap.clear();
399+
allocator.DestroyAll();
400+
LLVM_DEBUG(llvm::dbgs() << "invalidate all\n");
401+
}
402+
403+
void AccessStorageAnalysis::invalidate(
404+
SILFunction *F, InvalidationKind K) {
405+
if (FunctionInfo *FInfo = functionInfoMap.lookup(F)) {
406+
LLVM_DEBUG(llvm::dbgs() << " invalidate " << FInfo->F->getName() << '\n');
407+
invalidateIncludingAllCallers(FInfo);
408+
}
409+
}
410+
411+
void AccessStorageAnalysis::getCalleeEffects(
412+
FunctionAccessStorage &calleeEffects, FullApplySite fullApply) {
413+
if (calleeEffects.summarizeCall(fullApply))
414+
return;
415+
416+
auto callees = BCA->getCalleeList(fullApply);
417+
if (!callees.allCalleesVisible() ||
418+
// @callee_owned function calls implicitly release the context, which
419+
// may call deinits of boxed values.
420+
// TODO: be less conservative about what destructors might be called.
421+
fullApply.getOrigCalleeType()->isCalleeConsumed()) {
422+
calleeEffects.setWorstEffects();
423+
return;
424+
}
425+
426+
// We can see all the callees, so merge the effects from all of them.
427+
for (auto *callee : callees)
428+
calleeEffects.mergeFrom(getEffects(callee));
429+
}
430+
431+
void AccessStorageAnalysis::analyzeFunction(
432+
FunctionInfo *functionInfo, FunctionOrder &bottomUpOrder,
433+
int recursionDepth) {
434+
functionInfo->needUpdateCallers = true;
435+
436+
if (bottomUpOrder.prepareForVisiting(functionInfo))
437+
return;
438+
439+
auto *F = functionInfo->F;
440+
if (functionInfo->functionEffects.summarizeFunction(F))
441+
return;
442+
443+
LLVM_DEBUG(llvm::dbgs() << " >> analyze " << F->getName() << '\n');
444+
445+
// Check all instructions of the function
446+
for (auto &BB : *F) {
447+
for (auto &I : BB) {
448+
if (auto fullApply = FullApplySite::isa(&I))
449+
analyzeCall(functionInfo, fullApply, bottomUpOrder, recursionDepth);
450+
else
451+
functionInfo->functionEffects.analyzeInstruction(&I);
452+
}
453+
}
454+
LLVM_DEBUG(llvm::dbgs() << " << finished " << F->getName() << '\n');
455+
}
456+
457+
void AccessStorageAnalysis::analyzeCall(
458+
FunctionInfo *functionInfo, FullApplySite fullApply,
459+
FunctionOrder &bottomUpOrder, int recursionDepth) {
460+
461+
FunctionAccessStorage applyEffects;
462+
if (applyEffects.summarizeCall(fullApply)) {
463+
functionInfo->functionEffects.mergeFromApply(applyEffects, fullApply);
464+
return;
465+
}
466+
467+
if (recursionDepth >= MaxRecursionDepth) {
468+
functionInfo->functionEffects.setWorstEffects();
469+
return;
470+
}
471+
CalleeList callees = BCA->getCalleeList(fullApply);
472+
if (!callees.allCalleesVisible() ||
473+
// @callee_owned function calls implicitly release the context, which
474+
// may call deinits of boxed values.
475+
// TODO: be less conservative about what destructors might be called.
476+
fullApply.getOrigCalleeType()->isCalleeConsumed()) {
477+
functionInfo->functionEffects.setWorstEffects();
478+
return;
479+
}
480+
// Derive the effects of the apply from the known callees.
481+
// Defer merging callee effects until the callee is scheduled
482+
for (SILFunction *callee : callees) {
483+
FunctionInfo *calleeInfo = getFunctionInfo(callee);
484+
calleeInfo->addCaller(functionInfo, fullApply);
485+
if (!calleeInfo->isVisited()) {
486+
// Recursively visit the called function.
487+
analyzeFunction(calleeInfo, bottomUpOrder, recursionDepth + 1);
488+
bottomUpOrder.tryToSchedule(calleeInfo);
489+
}
490+
}
491+
}
492+
493+
void AccessStorageAnalysis::recompute(
494+
FunctionInfo *initialInfo) {
495+
allocNewUpdateID();
496+
497+
LLVM_DEBUG(llvm::dbgs() << "recompute function-effect analysis with UpdateID "
498+
<< getCurrentUpdateID() << '\n');
499+
500+
// Collect and analyze all functions to recompute, starting at initialInfo.
501+
FunctionOrder bottomUpOrder(getCurrentUpdateID());
502+
analyzeFunction(initialInfo, bottomUpOrder, 0);
503+
504+
// Build the bottom-up order.
505+
bottomUpOrder.tryToSchedule(initialInfo);
506+
bottomUpOrder.finishScheduling();
507+
508+
// Second step: propagate the side-effect information up the call-graph until
509+
// it stabilizes.
510+
bool needAnotherIteration;
511+
do {
512+
LLVM_DEBUG(llvm::dbgs() << "new iteration\n");
513+
needAnotherIteration = false;
514+
515+
for (FunctionInfo *functionInfo : bottomUpOrder) {
516+
if (!functionInfo->needUpdateCallers)
517+
continue;
518+
519+
LLVM_DEBUG(llvm::dbgs() << " update callers of "
520+
<< functionInfo->F->getName() << '\n');
521+
functionInfo->needUpdateCallers = false;
522+
523+
// Propagate the function effects to all callers.
524+
for (const auto &E : functionInfo->getCallers()) {
525+
assert(E.isValid());
526+
527+
// Only include callers which we are actually recomputing.
528+
if (!bottomUpOrder.wasRecomputedWithCurrentUpdateID(E.Caller))
529+
continue;
530+
531+
LLVM_DEBUG(llvm::dbgs() << " merge into caller "
532+
<< E.Caller->F->getName() << '\n');
533+
534+
if (E.Caller->functionEffects.mergeFromApply(
535+
functionInfo->functionEffects, FullApplySite(E.FAS))) {
536+
E.Caller->needUpdateCallers = true;
537+
if (!E.Caller->isScheduledAfter(functionInfo)) {
538+
// This happens if we have a cycle in the call-graph.
539+
needAnotherIteration = true;
540+
}
541+
}
542+
}
543+
}
544+
} while (needAnotherIteration);
545+
}
546+
396547
SILAnalysis *swift::createAccessStorageAnalysis(SILModule *) {
397548
return new AccessStorageAnalysis();
398549
}

0 commit comments

Comments
 (0)