From aa5ec34e312699a2fe5263133129a1e103ac91ee Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih Date: Thu, 2 Jul 2020 22:09:01 -0700 Subject: [PATCH] [LoopDeletion] Emit a remark when a dead loop is deleted This emits a remark when LoopDeletion deletes a dead loop, using the source location of the loop's header. There are currently two reasons for removing the loop: invariant loop or loop that never executes. Differential Revision: https://reviews.llvm.org/D83113 --- llvm/lib/Transforms/Scalar/LoopDeletion.cpp | 26 +++++++++++++-- llvm/test/Transforms/LoopDeletion/basic-remark.ll | 37 ++++++++++++++++++++++ .../Transforms/LoopDeletion/unreachable-loops.ll | 25 ++++++++++++++- 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 llvm/test/Transforms/LoopDeletion/basic-remark.ll diff --git a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp index 1949340..be209d3 100644 --- a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp +++ b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp @@ -19,6 +19,7 @@ #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/PatternMatch.h" #include "llvm/InitializePasses.h" @@ -136,7 +137,8 @@ static bool isLoopNeverExecuted(Loop *L) { /// instructions out of the loop. static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT, ScalarEvolution &SE, LoopInfo &LI, - MemorySSA *MSSA) { + MemorySSA *MSSA, + OptimizationRemarkEmitter &ORE) { assert(L->isLCSSAForm(DT) && "Expected LCSSA!"); // We can only remove the loop if there is a preheader that we can branch from @@ -166,6 +168,11 @@ static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT, std::fill(P.incoming_values().begin(), P.incoming_values().end(), UndefValue::get(P.getType())); } + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "NeverExecutes", L->getStartLoc(), + L->getHeader()) + << "Loop deleted because it never executes"; + }); deleteDeadLoop(L, &DT, &SE, &LI, MSSA); ++NumDeleted; return LoopDeletionResult::Deleted; @@ -202,6 +209,11 @@ static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT, } LLVM_DEBUG(dbgs() << "Loop is invariant, delete it!"); + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "Invariant", L->getStartLoc(), + L->getHeader()) + << "Loop deleted because it is invariant"; + }); deleteDeadLoop(L, &DT, &SE, &LI, MSSA); ++NumDeleted; @@ -215,7 +227,11 @@ PreservedAnalyses LoopDeletionPass::run(Loop &L, LoopAnalysisManager &AM, LLVM_DEBUG(dbgs() << "Analyzing Loop for deletion: "); LLVM_DEBUG(L.dump()); std::string LoopName = std::string(L.getName()); - auto Result = deleteLoopIfDead(&L, AR.DT, AR.SE, AR.LI, AR.MSSA); + // For the new PM, we can't use OptimizationRemarkEmitter as an analysis + // pass. Function analyses need to be preserved across loop transformations + // but ORE cannot be preserved (see comment before the pass definition). + OptimizationRemarkEmitter ORE(L.getHeader()->getParent()); + auto Result = deleteLoopIfDead(&L, AR.DT, AR.SE, AR.LI, AR.MSSA, ORE); if (Result == LoopDeletionResult::Unmodified) return PreservedAnalyses::all(); @@ -265,11 +281,15 @@ bool LoopDeletionLegacyPass::runOnLoop(Loop *L, LPPassManager &LPM) { MemorySSA *MSSA = nullptr; if (MSSAAnalysis) MSSA = &MSSAAnalysis->getMSSA(); + // For the old PM, we can't use OptimizationRemarkEmitter as an analysis + // pass. Function analyses need to be preserved across loop transformations + // but ORE cannot be preserved (see comment before the pass definition). + OptimizationRemarkEmitter ORE(L->getHeader()->getParent()); LLVM_DEBUG(dbgs() << "Analyzing Loop for deletion: "); LLVM_DEBUG(L->dump()); - LoopDeletionResult Result = deleteLoopIfDead(L, DT, SE, LI, MSSA); + LoopDeletionResult Result = deleteLoopIfDead(L, DT, SE, LI, MSSA, ORE); if (Result == LoopDeletionResult::Deleted) LPM.markLoopAsDeleted(*L); diff --git a/llvm/test/Transforms/LoopDeletion/basic-remark.ll b/llvm/test/Transforms/LoopDeletion/basic-remark.ll new file mode 100644 index 0000000..c56ae9d --- /dev/null +++ b/llvm/test/Transforms/LoopDeletion/basic-remark.ll @@ -0,0 +1,37 @@ +; RUN: opt -loop-deletion %s -o /dev/null --pass-remarks-output=%t --pass-remarks-filter=loop-delete +; RUN: cat %t | FileCheck %s + +; Check that we use the right debug location: the loop header. +; CHECK: --- !Passed +; CHECK-NEXT: Pass: loop-delete +; CHECK-NEXT: Name: Invariant +; CHECK-NEXT: DebugLoc: { File: loop.c, Line: 2, Column: 3 } +; CHECK-NEXT: Function: main +; CHECK-NEXT: Args: +; CHECK-NEXT: - String: Loop deleted because it is invariant +; CHECK-NEXT: ... +define i32 @main() local_unnamed_addr #0 { +entry: + br label %for.cond, !dbg !9 + +for.cond: + %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.cond ] + %cmp = icmp ult i32 %i.0, 1000 + %inc = add nuw nsw i32 %i.0, 1 + br i1 %cmp, label %for.cond, label %for.cond.cleanup + +for.cond.cleanup: + ret i32 0 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: NoDebug, enums: !2, nameTableKind: None, sysroot: "/") +!1 = !DIFile(filename: "loop.c", directory: "") +!2 = !{} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!6 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!7 = !DIFile(filename: "loop.c", directory: "") +!8 = !DISubroutineType(types: !2) +!9 = !DILocation(line: 2, column: 3, scope: !6) diff --git a/llvm/test/Transforms/LoopDeletion/unreachable-loops.ll b/llvm/test/Transforms/LoopDeletion/unreachable-loops.ll index 5bdaeb1..a74ddf9 100644 --- a/llvm/test/Transforms/LoopDeletion/unreachable-loops.ll +++ b/llvm/test/Transforms/LoopDeletion/unreachable-loops.ll @@ -1,4 +1,5 @@ -; RUN: opt < %s -loop-deletion -verify-dom-info -S | FileCheck %s +; RUN: opt < %s -loop-deletion -verify-dom-info --pass-remarks-output=%t --pass-remarks-filter=loop-delete -S | FileCheck %s +; RUN: cat %t | FileCheck %s --check-prefix=REMARKS ; Checking that we can delete loops that are never executed. ; We do not change the constant conditional branch statement (where the not-taken target @@ -10,6 +11,8 @@ define void @test1(i64 %n, i64 %m) nounwind { ; CHECK-LABEL: entry: ; CHECK-NEXT: br i1 true, label %return, label %bb.preheader ; CHECK-NOT: bb: +; REMARKS-LABEL: Function: test1 +; REMARKS: Loop deleted because it never executes entry: br i1 true, label %return, label %bb @@ -63,6 +66,8 @@ define i64 @test3(i64 %n, i64 %m, i64 %maybe_zero) nounwind { ; CHECK-LABEL: return: ; CHECK-NEXT: %x.lcssa = phi i64 [ 20, %entry ], [ %x.lcssa.ph, %return.loopexit ] ; CHECK-NEXT: ret i64 %x.lcssa +; REMARKS-LABEL: Function: test3 +; REMARKS: Loop deleted because it never executes entry: br i1 false, label %bb, label %return @@ -121,6 +126,8 @@ define void @test5(i64 %n, i64 %m, i1 %cond) nounwind { ; CHECK-LABEL: looppred2: ; CHECK-NEXT: br i1 true, label %return, label %bb.preheader ; CHECK-NOT: bb: +; REMARKS-LABEL: Function: test5 +; REMARKS: Loop deleted because it never executes entry: br i1 %cond, label %looppred1, label %looppred2 @@ -179,6 +186,8 @@ define i64 @test7(i64 %n) { ; CHECK-LABEL: L1Latch: ; CHECK-NEXT: %y = phi i64 [ %y.next, %L1 ], [ %y.L2.lcssa, %L1Latch.loopexit ] ; CHECK: br i1 %cond2, label %exit, label %L1 +; REMARKS-LABEL: Function: test7 +; REMARKS: Loop deleted because it never executes entry: br label %L1 @@ -213,6 +222,8 @@ define void @test8(i64 %n) { ; CHECK-NEXT: br label %exit ; CHECK-LABEL: exit: ; CHECK-NEXT: ret void +; REMARKS-LABEL: Function: test8 +; REMARKS: Loop deleted because it never executes entry: br label %L1 @@ -246,6 +257,8 @@ define void @test9(i64 %n) { ; CHECK-NEXT: br label %L3 ; CHECK-LABEL: L3: ; CHECK: br i1 %cond2, label %L3, label %L1.loopexit +; REMARKS-LABEL: Function: test9 +; REMARKS: Loop deleted because it never executes entry: br label %L1 @@ -311,6 +324,12 @@ define void @test11(i64 %n) { ; CHECK-NEXT: br label %exit ; CHECK-LABEL: exit: ; CHECK-NEXT: ret void +; REMARKS-LABEL: Function: test11 +; REMARKS: Loop deleted because it is invariant +; REMARKS-LABEL: Function: test11 +; REMARKS: Loop deleted because it never executes +; REMARKS-LABEL: Function: test11 +; REMARKS: Loop deleted because it is invariant entry: br label %L1 @@ -345,6 +364,8 @@ define i64 @test12(i64 %n){ ; CHECK-LABEL: exit: ; CHECK-NEXT: %y.phi = phi i64 [ undef, %L1.preheader ] ; CHECK-NEXT: ret i64 %y.phi +; REMARKS-LABEL: Function: test12 +; REMARKS: Loop deleted because it never executes entry: br i1 true, label %exit1, label %L1 @@ -380,6 +401,8 @@ define i64 @test13(i64 %n) { ; CHECK-LABEL: exit: ; CHECK-NEXT: %y.phi = phi i64 [ undef, %L1.preheader ] ; CHECK-NEXT: ret i64 %y.phi +; REMARKS-LABEL: Function: test13 +; REMARKS: Loop deleted because it never executes entry: br i1 true, label %exit1, label %L1 -- 2.7.4