[Attributor] Add interface to emit remarks in Attributor
authorJoseph Huber <jhuber6@vols.utk.edu>
Mon, 7 Jun 2021 18:31:40 +0000 (14:31 -0400)
committerHuber, Joseph <huberjn@ornl.gov>
Tue, 22 Jun 2021 18:12:46 +0000 (14:12 -0400)
Summary:
This patch adds support for the Attributor to emit remarks on behalf of some
other pass. The attributor can now optionally take a callback function that
returns an OptimizationRemarkEmitter object when given a Function pointer. If
this is availible then a remark will be emitted for the corresponding pass
name.

Depends on D102197

Reviewed By: sstefan1 thegameg

Differential Revision: https://reviews.llvm.org/D102444

llvm/include/llvm/Transforms/IPO/Attributor.h
llvm/lib/Transforms/IPO/AttributorAttributes.cpp
llvm/lib/Transforms/IPO/OpenMPOpt.cpp
llvm/test/Transforms/OpenMP/remove_globalization.ll
llvm/test/Transforms/OpenMP/replace_globalization.ll

index a3e1905..182a794 100644 (file)
 #include "llvm/Analysis/LazyCallGraph.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/MustExecute.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/PostDominators.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/IR/AbstractCallSite.h"
@@ -1076,6 +1077,10 @@ private:
 /// NOTE: The mechanics of adding a new "concrete" abstract attribute are
 ///       described in the file comment.
 struct Attributor {
+
+  using OptimizationRemarkGetter =
+      function_ref<OptimizationRemarkEmitter &(Function *)>;
+
   /// Constructor
   ///
   /// \param Functions The set of functions we are deriving attributes for.
@@ -1091,7 +1096,29 @@ struct Attributor {
              bool RewriteSignatures = true)
       : Allocator(InfoCache.Allocator), Functions(Functions),
         InfoCache(InfoCache), CGUpdater(CGUpdater), Allowed(Allowed),
-        DeleteFns(DeleteFns), RewriteSignatures(RewriteSignatures) {}
+        DeleteFns(DeleteFns), RewriteSignatures(RewriteSignatures),
+        OREGetter(None), PassName("") {}
+
+  /// Constructor
+  ///
+  /// \param Functions The set of functions we are deriving attributes for.
+  /// \param InfoCache Cache to hold various information accessible for
+  ///                  the abstract attributes.
+  /// \param CGUpdater Helper to update an underlying call graph.
+  /// \param Allowed If not null, a set limiting the attribute opportunities.
+  /// \param DeleteFns Whether to delete functions
+  /// \param OREGetter A callback function that returns an ORE object from a
+  ///                  Function pointer.
+  /// \param PassName  The name of the pass emitting remarks.
+  Attributor(SetVector<Function *> &Functions, InformationCache &InfoCache,
+             CallGraphUpdater &CGUpdater, DenseSet<const char *> *Allowed,
+             bool DeleteFns, bool RewriteSignatures,
+             OptimizationRemarkGetter OREGetter, const char *PassName)
+      : Allocator(InfoCache.Allocator), Functions(Functions),
+        InfoCache(InfoCache), CGUpdater(CGUpdater), Allowed(Allowed),
+        DeleteFns(DeleteFns), RewriteSignatures(RewriteSignatures),
+        OREGetter(Optional<OptimizationRemarkGetter>(OREGetter)),
+        PassName(PassName) {}
 
   ~Attributor();
 
@@ -1494,6 +1521,41 @@ public:
                        const AbstractAttribute &QueryingAA, const Value &V,
                        DepClassTy LivenessDepClass = DepClassTy::OPTIONAL);
 
+  /// Emit a remark generically.
+  ///
+  /// This template function can be used to generically emit a remark. The
+  /// RemarkKind should be one of the following:
+  ///   - OptimizationRemark to indicate a successful optimization attempt
+  ///   - OptimizationRemarkMissed to report a failed optimization attempt
+  ///   - OptimizationRemarkAnalysis to provide additional information about an
+  ///     optimization attempt
+  ///
+  /// The remark is built using a callback function \p RemarkCB that takes a
+  /// RemarkKind as input and returns a RemarkKind.
+  template <typename RemarkKind, typename RemarkCallBack>
+  void emitRemark(Instruction *I, StringRef RemarkName,
+                  RemarkCallBack &&RemarkCB) const {
+    if (!OREGetter)
+      return;
+
+    Function *F = I->getFunction();
+    auto &ORE = OREGetter.getValue()(F);
+
+    ORE.emit([&]() { return RemarkCB(RemarkKind(PassName, RemarkName, I)); });
+  }
+
+  /// Emit a remark on a function.
+  template <typename RemarkKind, typename RemarkCallBack>
+  void emitRemark(Function *F, StringRef RemarkName,
+                  RemarkCallBack &&RemarkCB) const {
+    if (!OREGetter)
+      return;
+
+    auto &ORE = OREGetter.getValue()(F);
+
+    ORE.emit([&]() { return RemarkCB(RemarkKind(PassName, RemarkName, F)); });
+  }
+
   /// Helper struct used in the communication between an abstract attribute (AA)
   /// that wants to change the signature of a function and the Attributor which
   /// applies the changes. The struct is partially initialized with the
@@ -1830,6 +1892,12 @@ private:
   SmallDenseSet<WeakVH, 8> ToBeDeletedInsts;
   ///}
 
+  /// Callback to get an OptimizationRemarkEmitter from a Function *.
+  Optional<OptimizationRemarkGetter> OREGetter;
+
+  /// The name of the pass to emit remarks for.
+  const char *PassName = "";
+
   friend AADepGraph;
   friend AttributorCallGraph;
 };
index 17d4870..b5221b1 100644 (file)
@@ -5048,6 +5048,17 @@ struct AAHeapToStackImpl : public AAHeapToStack {
       LLVM_DEBUG(dbgs() << "H2S: Removing malloc call: " << *MallocCall
                         << "\n");
 
+      auto Remark = [&](OptimizationRemark OR) {
+        LibFunc IsAllocShared;
+        if (auto *CB = dyn_cast<CallBase>(MallocCall)) {
+          TLI->getLibFunc(*CB, IsAllocShared);
+          if (IsAllocShared == LibFunc___kmpc_alloc_shared)
+            return OR << "Moving globalized variable to the stack.";
+        }
+        return OR << "Moving memory allocation from the heap to the stack.";
+      };
+      A.emitRemark<OptimizationRemark>(MallocCall, "HeapToStack", Remark);
+
       Align Alignment;
       Value *Size;
       if (isCallocLikeFn(MallocCall, TLI)) {
@@ -5194,6 +5205,23 @@ ChangeStatus AAHeapToStackImpl::updateImpl(Attributor &A) {
 
         if (!NoCaptureAA.isAssumedNoCapture() ||
             !ArgNoFreeAA.isAssumedNoFree()) {
+
+          // Emit a missed remark if this is missed OpenMP globalization.
+          auto Remark = [&](OptimizationRemarkMissed ORM) {
+            return ORM << "Could not move globalized variable to the stack. "
+                       << "Variable is potentially "
+                       << ((!NoCaptureAA.isAssumedNoCapture()) ? "captured."
+                                                               : "freed.");
+          };
+
+          LibFunc IsAllocShared;
+          if (auto *AllocShared = dyn_cast<CallBase>(&I)) {
+            TLI->getLibFunc(*AllocShared, IsAllocShared);
+            if (IsAllocShared == LibFunc___kmpc_alloc_shared)
+              A.emitRemark<OptimizationRemarkMissed>(
+                  AllocShared, "HeapToStackFailed", Remark);
+          }
+
           LLVM_DEBUG(dbgs() << "[H2S] Bad user: " << *UserI << "\n");
           ValidUsesOnly = false;
         }
index 6270f71..b6b8219 100644 (file)
@@ -2503,6 +2503,15 @@ struct AAHeapToSharedFunction : public AAHeapToShared {
       auto *NewBuffer =
           ConstantExpr::getPointerCast(SharedMem, Int8Ty->getPointerTo());
 
+      auto Remark = [&](OptimizationRemark OR) {
+        return OR << "Replaced globalized variable with "
+                  << ore::NV("SharedMemory", AllocSize->getZExtValue())
+                  << ((AllocSize->getZExtValue() != 1) ? " bytes " : " byte ")
+                  << "of shared memory";
+      };
+      A.emitRemark<OptimizationRemark>(CB, "OpenMPReplaceGlobalization",
+                                       Remark);
+
       SharedMem->setAlignment(MaybeAlign(32));
 
       A.changeValueAfterManifest(*CB, *NewBuffer);
@@ -2657,7 +2666,8 @@ PreservedAnalyses OpenMPOptPass::run(Module &M, ModuleAnalysisManager &AM) {
   OMPInformationCache InfoCache(M, AG, Allocator, /*CGSCC*/ Functions,
                                 OMPInModule.getKernels());
 
-  Attributor A(Functions, InfoCache, CGUpdater, nullptr, true, false);
+  Attributor A(Functions, InfoCache, CGUpdater, nullptr, true, false, OREGetter,
+               DEBUG_TYPE);
 
   OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A);
   bool Changed = OMPOpt.run(true);
@@ -2712,7 +2722,8 @@ PreservedAnalyses OpenMPOptCGSCCPass::run(LazyCallGraph::SCC &C,
   OMPInformationCache InfoCache(*(Functions.back()->getParent()), AG, Allocator,
                                 /*CGSCC*/ Functions, OMPInModule.getKernels());
 
-  Attributor A(Functions, InfoCache, CGUpdater, nullptr, false);
+  Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true, OREGetter,
+               DEBUG_TYPE);
 
   OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A);
   bool Changed = OMPOpt.run(false);
@@ -2788,7 +2799,8 @@ struct OpenMPOptCGSCCLegacyPass : public CallGraphSCCPass {
         *(Functions.back()->getParent()), AG, Allocator,
         /*CGSCC*/ Functions, OMPInModule.getKernels());
 
-    Attributor A(Functions, InfoCache, CGUpdater, nullptr, false);
+    Attributor A(Functions, InfoCache, CGUpdater, nullptr, false, true,
+                 OREGetter, DEBUG_TYPE);
 
     OpenMPOpt OMPOpt(SCC, CGUpdater, OREGetter, InfoCache, A);
     return OMPOpt.run(false);
index 837851f..18a7bb1 100644 (file)
@@ -1,8 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
 ; RUN: opt -S -passes=openmp-opt < %s | FileCheck %s
+; RUN: opt -passes=openmp-opt -pass-remarks=openmp-opt -pass-remarks-missed=openmp-opt -disable-output < %s 2>&1 | FileCheck %s -check-prefix=CHECK-REMARKS
 target datalayout = "e-i64:64-i128:128-v16:16-v32:32-n16:32:64"
 target triple = "nvptx64"
 
+; CHECK-REMARKS: remark: remove_globalization.c:4:2: Could not move globalized variable to the stack. Variable is potentially captured.
+; CHECK-REMARKS: remark: remove_globalization.c:2:2: Moving globalized variable to the stack.
+
 @S = external local_unnamed_addr global i8*
 
 define void @kernel() {
@@ -26,7 +30,7 @@ define internal void @foo() {
 ; CHECK-NEXT:    ret void
 ;
 entry:
-  %0 = call i8* @__kmpc_alloc_shared(i64 4)
+  %0 = call i8* @__kmpc_alloc_shared(i64 4), !dbg !9
   call void @use(i8* %0)
   call void @__kmpc_free_shared(i8* %0)
   ret void
@@ -42,7 +46,7 @@ define internal void @bar() {
 ; CHECK-NEXT:    ret void
 ;
 entry:
-  %0 = call i8* @__kmpc_alloc_shared(i64 4)
+  %0 = call i8* @__kmpc_alloc_shared(i64 4), !dbg !10
   call void @share(i8* %0)
   call void @__kmpc_free_shared(i8* %0)
   ret void
@@ -81,3 +85,8 @@ declare void @__kmpc_free_shared(i8*)
 !3 = !{i32 2, !"Debug Info Version", i32 3}
 !4 = !{i32 1, !"wchar_size", i32 4}
 !5 = !{void ()* @kernel, !"kernel", i32 1}
+!6 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!7 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !2)
+!9 = !DILocation(line: 2, column: 2, scope: !6)
+!10 = !DILocation(line: 4, column: 2, scope: !7)
index d50f1f7..d195ece 100644 (file)
@@ -1,9 +1,12 @@
 ; RUN: opt -S -passes='openmp-opt' < %s | FileCheck %s
+; RUN: opt -passes=openmp-opt -pass-remarks=openmp-opt -disable-output < %s 2>&1 | FileCheck %s -check-prefix=CHECK-REMARKS
 target datalayout = "e-i64:64-i128:128-v16:16-v32:32-n16:32:64"
 target triple = "nvptx64"
 
 @S = external local_unnamed_addr global i8*
 
+; CHECK-REMARKS: remark: replace_globalization.c:5:7: Replaced globalized variable with 16 bytes of shared memory
+; CHECK-REMARKS: remark: replace_globalization.c:5:14: Replaced globalized variable with 4 bytes of shared memory
 ; CHECK: [[SHARED_X:@.+]] = internal addrspace(3) global [16 x i8] undef
 ; CHECK: [[SHARED_Y:@.+]] = internal addrspace(3) global [4 x i8] undef
 
@@ -68,7 +71,6 @@ exit:
 
 define void @use(i8* %x) {
 entry:
-  %addr = alloca i8*
   store i8* %x, i8** @S
   ret void
 }