diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -830,6 +830,9 @@ /// unrolling pass is run more than once (which it generally is). void setLoopAlreadyUnrolled(); + /// Add llvm.loop.mustprogress to this loop's loop id metadata. + void setLoopMustProgress(); + void dump() const; void dumpVerbose() const; diff --git a/llvm/include/llvm/Transforms/Utils/Cloning.h b/llvm/include/llvm/Transforms/Utils/Cloning.h --- a/llvm/include/llvm/Transforms/Utils/Cloning.h +++ b/llvm/include/llvm/Transforms/Utils/Cloning.h @@ -176,9 +176,9 @@ function_ref GetAssumptionCache = nullptr, ProfileSummaryInfo *PSI = nullptr, BlockFrequencyInfo *CallerBFI = nullptr, - BlockFrequencyInfo *CalleeBFI = nullptr) + BlockFrequencyInfo *CalleeBFI = nullptr, LoopInfo *CallerLI = nullptr) : CG(cg), GetAssumptionCache(GetAssumptionCache), PSI(PSI), - CallerBFI(CallerBFI), CalleeBFI(CalleeBFI) {} + CallerBFI(CallerBFI), CalleeBFI(CalleeBFI), CallerLI(CallerLI) {} /// If non-null, InlineFunction will update the callgraph to reflect the /// changes it makes. @@ -186,6 +186,7 @@ function_ref GetAssumptionCache; ProfileSummaryInfo *PSI; BlockFrequencyInfo *CallerBFI, *CalleeBFI; + LoopInfo *CallerLI; /// InlineFunction fills this in with all static allocas that get copied into /// the caller. diff --git a/llvm/lib/Analysis/LoopInfo.cpp b/llvm/lib/Analysis/LoopInfo.cpp --- a/llvm/lib/Analysis/LoopInfo.cpp +++ b/llvm/lib/Analysis/LoopInfo.cpp @@ -535,6 +535,22 @@ setLoopID(NewLoopID); } +void Loop::setLoopMustProgress() { + LLVMContext &Context = getHeader()->getContext(); + + MDNode *MustProgress = findOptionMDForLoop(this, "llvm.loop.mustprogress"); + + if (MustProgress) + return; + + MDNode *MustProgressMD = + MDNode::get(Context, MDString::get(Context, "llvm.loop.mustprogress")); + MDNode *LoopID = getLoopID(); + MDNode *NewLoopID = + makePostTransformationMetadata(Context, LoopID, {}, {MustProgressMD}); + setLoopID(NewLoopID); +} + bool Loop::isAnnotatedParallel() const { MDNode *DesiredLoopIdMetadata = getLoopID(); diff --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp --- a/llvm/lib/Transforms/IPO/Inliner.cpp +++ b/llvm/lib/Transforms/IPO/Inliner.cpp @@ -33,6 +33,7 @@ #include "llvm/Analysis/InlineAdvisor.h" #include "llvm/Analysis/InlineCost.h" #include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -846,7 +847,8 @@ InlineFunctionInfo IFI( /*cg=*/nullptr, GetAssumptionCache, PSI, &FAM.getResult(*(CB->getCaller())), - &FAM.getResult(Callee)); + &FAM.getResult(Callee), + &FAM.getResult(*(CB->getCaller()))); InlineResult IR = InlineFunction(*CB, IFI); if (!IR.isSuccess()) { diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -27,11 +27,12 @@ #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ProfileSummaryInfo.h" -#include "llvm/Transforms/Utils/Local.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/VectorUtils.h" #include "llvm/IR/Argument.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constant.h" @@ -61,6 +62,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include #include @@ -2300,6 +2302,20 @@ IFI.InlinedCallSites.push_back(CB); } + // Before we inline the basic blocks from the callee, we should check if the + // callee function has the maynotprogress attribute and if it does, we need + // to iterate through the caller function blocks (if it isn't maynotprogress) + // to attach the mustprogress loop metadata to each loop. + if (CB.getCalledFunction()->hasFnAttribute(Attribute::MayNotProgress) && + !CB.getCaller()->hasFnAttribute(Attribute::MayNotProgress) && + IFI.CallerLI) { + for (BasicBlock &BB : + make_range(CB.getCaller()->begin(), CB.getCaller()->end())) { + if (Loop *L = IFI.CallerLI->getLoopFor(&BB)) + L->setLoopMustProgress(); + } + } + // If we cloned in _exactly one_ basic block, and if that block ends in a // return instruction, we splice the body of the inlined callee directly into // the calling basic block. diff --git a/llvm/test/Transforms/Inline/inlined-mustprogress-loop-metadata.ll b/llvm/test/Transforms/Inline/inlined-mustprogress-loop-metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/inlined-mustprogress-loop-metadata.ll @@ -0,0 +1,60 @@ +; RUN: opt < %s -passes="inline" -S | FileCheck %s + +define void @callee(i32 %a, i32 %b) #0 { +entry: + br label %for.cond +for.cond: + %cmp = icmp slt i32 %a, %b + br i1 %cmp, label %for.body, label %for.end +for.body: + br label %for.cond, !llvm.loop !2 +for.end: + br label %while.body +while.body: + br label %while.body +} + +; CHECK: define void @caller(i32 [[VAR1:%.*]], i32 [[VAR2:%.*]]) [[ATTR1:#.*]] { +; CHECK: entry: +; CHECK: br label %[[FOR_COND:.*]] +; CHECK: [[FOR_COND]]: +; CHECK: [[CMP:%.*]] = icmp slt i32 [[VAR1]], [[VAR2]] +; CHECK: br i1 [[CMP]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK: br label %[[FOR_COND]], !llvm.loop [[LOOP4:!.*]] +; CHECK: [[FOR_END]]: +; CHECK: br label %[[FOR_COND1:.*]] +; CHECK: [[FOR_COND1]]: +; CHECK: br label %[[FOR_COND1]], !llvm.loop [[LOOP2:!.*]] + +define void @caller(i32 %a, i32 %b) #1 { +entry: + br label %for.cond +for.cond: + %cmp = icmp slt i32 %a, %b + br i1 %cmp, label %for.body, label %for.end +for.body: + br label %for.cond +for.end: + call void @callee(i32 0, i32 5) + ret void +} + +; CHECK: attributes [[ATTR1]] = { noinline maynotprogress } +; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[GEN3:!.*]]} +; CHECK: [[GEN3]] = !{!"llvm.loop.mustprogress"} +; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[GEN3]]} + +define i32 @main() #2 { +entry: + call void @caller(i32 0, i32 5) + ret i32 0 +} + +attributes #0 = { maynotprogress } +attributes #1 = { noinline } +attributes #2 = { noinline } + + +!2 = distinct !{!2, !3} +!3 = !{!"llvm.loop.mustprogress"} diff --git a/llvm/test/Transforms/Inline/maynotprogress.ll b/llvm/test/Transforms/Inline/maynotprogress.ll deleted file mode 100644 --- a/llvm/test/Transforms/Inline/maynotprogress.ll +++ /dev/null @@ -1,42 +0,0 @@ -; RUN: opt -inline -S < %s | FileCheck %s - -;; Original C Code: -;; void callee(int a, int b) { -;; for (;a < b;) ; -;; while(1) ; -;; } -;; void caller() { -;; callee(0, 5); -;; } - -define void @callee(i32 %a, i32 %b) #0 { -entry: - br label %for.cond -for.cond: - %cmp = icmp slt i32 %a, %b - br i1 %cmp, label %for.body, label %for.end -for.body: - br label %for.cond -for.end: - br label %while.body -while.body: - br label %while.body -} - -; CHECK: define void @caller() [[ATTR0:#.*]] { -; CHECK: entry: -; CHECK: br label %[[FOR_COND:.*]] -; CHECK: [[FOR_COND]]: -; CHECK: br label %[[FOR_COND]] -; CHECK: ret void -; CHECK: } - -define void @caller() { -entry: - call void @callee(i32 0, i32 5) - ret void -} - -; CHECK: attributes [[ATTR0]] = { maynotprogress } - -attributes #0 = { maynotprogress }