Index: llvm/include/llvm/Analysis/GlobalsModRef.h =================================================================== --- llvm/include/llvm/Analysis/GlobalsModRef.h +++ llvm/include/llvm/Analysis/GlobalsModRef.h @@ -106,6 +106,10 @@ /// case the most generic behavior of this function should be returned. MemoryEffects getMemoryEffects(const Function *F); + /// Passes that add a call to llvm.assume into a function \p F need to update + /// the cached GlobalsAAResult via this function. + void addAssumptionCall(const Function *F); + private: FunctionInfo *getFunctionInfo(const Function *F); Index: llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h =================================================================== --- llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h +++ llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h @@ -19,6 +19,7 @@ namespace llvm { class AssumptionCache; +class GlobalsAAResult; struct SimplifyCFGOptions { int BonusInstThreshold = 1; @@ -32,6 +33,7 @@ bool SpeculateBlocks = true; AssumptionCache *AC = nullptr; + GlobalsAAResult *GAAResult = nullptr; // Support 'builder' pattern to set members by name at construction time. SimplifyCFGOptions &bonusInstThreshold(int I) { Index: llvm/lib/Analysis/GlobalsModRef.cpp =================================================================== --- llvm/lib/Analysis/GlobalsModRef.cpp +++ llvm/lib/Analysis/GlobalsModRef.cpp @@ -237,6 +237,11 @@ // This object is now destroyed! } +void GlobalsAAResult::addAssumptionCall(const Function *F) { + if (FunctionInfo *FI = getFunctionInfo(F)) + FI->addModRefInfo(ModRefInfo::ModRef); +} + MemoryEffects GlobalsAAResult::getMemoryEffects(const Function *F) { if (FunctionInfo *FI = getFunctionInfo(F)) return MemoryEffects(FI->getModRefInfo()); Index: llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -360,6 +360,11 @@ DominatorTree *DT = nullptr; if (RequireAndPreserveDomTree) DT = &AM.getResult(F); + + auto *MAM = &AM.getResult(F); + Options.GAAResult = + MAM ? MAM->getCachedResult(*F.getParent()) : nullptr; + if (!simplifyFunctionCFG(F, TTI, DT, Options)) return PreservedAnalyses::all(); PreservedAnalyses PA; Index: llvm/lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -27,6 +27,7 @@ #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/GuardUtils.h" #include "llvm/Analysis/InstructionSimplify.h" #include "llvm/Analysis/MemorySSA.h" @@ -3132,10 +3133,9 @@ /// If we have a conditional branch on something for which we know the constant /// value in predecessors (e.g. a phi node in the current block), thread edges /// from the predecessor to their ultimate destination. -static std::optional -FoldCondBranchOnValueKnownInPredecessorImpl(BranchInst *BI, DomTreeUpdater *DTU, - const DataLayout &DL, - AssumptionCache *AC) { +static std::optional FoldCondBranchOnValueKnownInPredecessorImpl( + BranchInst *BI, DomTreeUpdater *DTU, const DataLayout &DL, + AssumptionCache *AC, GlobalsAAResult *GAA) { SmallMapVector, 2> KnownValues; BasicBlock *BB = BI->getParent(); Value *Cond = BI->getCondition(); @@ -3242,9 +3242,12 @@ } if (N) { // Register the new instruction with the assumption cache if necessary. - if (auto *Assume = dyn_cast(N)) + if (auto *Assume = dyn_cast(N)) { if (AC) AC->registerAssumption(Assume); + if (GAA) + GAA->addAssumptionCall(BB->getParent()); + } } } @@ -3273,15 +3276,15 @@ return false; } -static bool FoldCondBranchOnValueKnownInPredecessor(BranchInst *BI, - DomTreeUpdater *DTU, - const DataLayout &DL, - AssumptionCache *AC) { +static bool FoldCondBranchOnValueKnownInPredecessor( + BranchInst *BI, DomTreeUpdater *DTU, const DataLayout &DL, + AssumptionCache *AC, GlobalsAAResult *GAAResult) { std::optional Result; bool EverChanged = false; do { // Note that None means "we changed things, but recurse further." - Result = FoldCondBranchOnValueKnownInPredecessorImpl(BI, DTU, DL, AC); + Result = + FoldCondBranchOnValueKnownInPredecessorImpl(BI, DTU, DL, AC, GAAResult); EverChanged |= Result == std::nullopt || *Result; } while (Result == std::nullopt); return EverChanged; @@ -5146,6 +5149,8 @@ } if (Options.AC) Options.AC->registerAssumption(cast(Assumption)); + if (Options.GAAResult) + Options.GAAResult->addAssumptionCall(Assumption->getCaller()); EraseTerminatorAndDCECond(BI); Changed = true; @@ -7082,7 +7087,8 @@ // If this is a branch on something for which we know the constant value in // predecessors (e.g. a phi node in the current block), thread control // through this block. - if (FoldCondBranchOnValueKnownInPredecessor(BI, DTU, DL, Options.AC)) + if (FoldCondBranchOnValueKnownInPredecessor(BI, DTU, DL, Options.AC, + Options.GAAResult)) return requestResimplify(); // Scan predecessor blocks for conditional branches. Index: llvm/test/Analysis/GlobalsModRef/simplify_cfg_add_assume_call.ll =================================================================== --- /dev/null +++ llvm/test/Analysis/GlobalsModRef/simplify_cfg_add_assume_call.ll @@ -0,0 +1,64 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 +; RUN: opt < %s -passes="require,simplifycfg,function-attrs" -S | FileCheck %s + +; Simplify CFG will add a call to llvm.assume which we need to model in the cached globals-aa result. +; Make sure we do not set memory(none) but memory(inaccessiblemem: readwrite), or avoid emitting the assume call. + +; See https://github.com/llvm/llvm-project/issues/63925 + +define internal void @foo(ptr %p) { +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) +; CHECK-LABEL: define internal void @foo +; CHECK-SAME: (ptr readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: ok: +; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[P]], null +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[C]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: ret void +; + %c = icmp eq ptr %p, null + br i1 %c, label %err, label %ok +err: + unreachable +ok: + ret void +} + +define internal void @bar() { +; CHECK: Function Attrs: mustprogress willreturn memory(inaccessiblemem: readwrite) +; CHECK-LABEL: define internal void @bar +; CHECK-SAME: () #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: call void @foo() +; CHECK-NEXT: ret void +; + call void @foo() + ret void +} + +define internal void @baz() { +; CHECK: Function Attrs: mustprogress willreturn memory(inaccessiblemem: readwrite) +; CHECK-LABEL: define internal void @baz +; CHECK-SAME: () #[[ATTR1]] { +; CHECK-NEXT: call void @bar() +; CHECK-NEXT: ret void +; + call void @bar() + ret void +} + +define void @extern() { +; CHECK: Function Attrs: mustprogress willreturn memory(inaccessiblemem: readwrite) +; CHECK-LABEL: define void @extern +; CHECK-SAME: () #[[ATTR1]] { +; CHECK-NEXT: call void @baz() +; CHECK-NEXT: ret void +; + call void @baz() + ret void +} + +;. +; CHECK: attributes #[[ATTR0]] = { mustprogress nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) } +; CHECK: attributes #[[ATTR1]] = { mustprogress willreturn memory(inaccessiblemem: readwrite) } +; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) } +;. Index: llvm/test/Other/new-pm-defaults.ll =================================================================== --- llvm/test/Other/new-pm-defaults.ll +++ llvm/test/Other/new-pm-defaults.ll @@ -101,6 +101,7 @@ ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running analysis: AssumptionAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SROAPass ; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis ; CHECK-O-NEXT: Running pass: EarlyCSEPass @@ -118,7 +119,6 @@ ; CHECK-O-NEXT: Running analysis: BasicAA ; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA ; CHECK-O-NEXT: Running analysis: TypeBasedAA -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass Index: llvm/test/Other/new-pm-thinlto-prelink-defaults.ll =================================================================== --- llvm/test/Other/new-pm-thinlto-prelink-defaults.ll +++ llvm/test/Other/new-pm-thinlto-prelink-defaults.ll @@ -74,6 +74,7 @@ ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running analysis: AssumptionAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SROAPass ; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis ; CHECK-O-NEXT: Running pass: EarlyCSEPass @@ -90,7 +91,6 @@ ; CHECK-O-NEXT: Running analysis: BasicAA ; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA ; CHECK-O-NEXT: Running analysis: TypeBasedAA -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass ; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis Index: llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll =================================================================== --- llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll +++ llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll @@ -38,6 +38,7 @@ ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running analysis: AssumptionAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SROAPass ; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis ; CHECK-O-NEXT: Running pass: EarlyCSEPass @@ -54,7 +55,6 @@ ; CHECK-O-NEXT: Running analysis: BasicAA ; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA ; CHECK-O-NEXT: Running analysis: TypeBasedAA -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass ; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis Index: llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll =================================================================== --- llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll +++ llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll @@ -37,6 +37,7 @@ ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running analysis: AssumptionAnalysis +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running pass: SROAPass ; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis ; CHECK-O-NEXT: Running pass: EarlyCSEPass @@ -57,7 +58,6 @@ ; CHECK-O-NEXT: Running analysis: BasicAA ; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA ; CHECK-O-NEXT: Running analysis: TypeBasedAA -; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy ; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on foo ; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on foo ; CHECK-O-NEXT: Running analysis: LoopAnalysis on foo Index: llvm/unittests/IR/PassManagerTest.cpp =================================================================== --- llvm/unittests/IR/PassManagerTest.cpp +++ llvm/unittests/IR/PassManagerTest.cpp @@ -8,6 +8,7 @@ #include "llvm/IR/PassManager.h" #include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" @@ -831,6 +832,8 @@ SI.registerCallbacks(PIC, &MAM); MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); + MAM.registerPass([&] { return GlobalsAA(); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); FAM.registerPass([&] { return DominatorTreeAnalysis(); }); FAM.registerPass([&] { return AssumptionAnalysis(); }); @@ -880,6 +883,8 @@ SI.registerCallbacks(PIC, &MAM); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); + MAM.registerPass([&] { return GlobalsAA(); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); FAM.registerPass([&] { return DominatorTreeAnalysis(); }); FAM.registerPass([&] { return AssumptionAnalysis(); }); @@ -948,6 +953,8 @@ SI.registerCallbacks(PIC, &MAM); MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); }); MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); + MAM.registerPass([&] { return GlobalsAA(); }); + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); }); FAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); }); FAM.registerPass([&] { return DominatorTreeAnalysis(); }); FAM.registerPass([&] { return AssumptionAnalysis(); });