avt77 updated this revision to Diff 156064.
avt77 added a comment.

I added required comments and did the required changes.


https://reviews.llvm.org/D47196

Files:
  include/clang/Driver/CC1Options.td
  include/clang/Frontend/CodeGenOptions.h
  include/clang/Frontend/Utils.h
  lib/CodeGen/CodeGenAction.cpp
  lib/CodeGen/CodeGenFunction.cpp
  lib/CodeGen/CodeGenModule.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Frontend/FrontendTiming.cpp
  lib/Parse/CMakeLists.txt
  lib/Parse/ParseTemplate.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaLambda.cpp
  lib/Sema/TreeTransform.h
  test/Frontend/ftime-report-template-decl.cpp
  test/Headers/opencl-c-header.cl

Index: test/Headers/opencl-c-header.cl
===================================================================
--- test/Headers/opencl-c-header.cl
+++ test/Headers/opencl-c-header.cl
@@ -71,4 +71,5 @@
 }
 #endif //__OPENCL_C_VERSION__
 
-// CHECK-MOD: Reading modules
+// CHECK-DAG-MOD: Clang Timers: CodeGen Functions
+// CHECK-DAG-MOD: Reading modules
Index: test/Frontend/ftime-report-template-decl.cpp
===================================================================
--- test/Frontend/ftime-report-template-decl.cpp
+++ test/Frontend/ftime-report-template-decl.cpp
@@ -3,9 +3,15 @@
 
 // Template function declarations
 template <typename T>
-void foo();
+T foo(T bar) {
+  T Result = bar * bar + bar / 1.2 + bar;
+  return Result;
+};
 template <typename T, typename U>
-void foo();
+T foo(T bar, U bar2) {
+  T Result = bar2 * bar + bar / 1.2 + bar2;
+  return Result;
+};
 
 // Template function definitions.
 template <typename T>
@@ -130,9 +136,15 @@
 template <typename U>
 oneT L<0>::O<char>::Fun(U) { return one; }
 
-void Instantiate() {
+double Instantiate() {
   sassert(sizeof(L<0>::O<int>::Fun(0)) == sizeof(one));
   sassert(sizeof(L<0>::O<char>::Fun(0)) == sizeof(one));
+  int R1 = foo<int>(123) + foo<double>(177.2) - foo<double>(331.442);
+  char R2 = foo<char, int>('d', 1234) * foo<double>(1.26);
+  int R3 = foo<double>(1.2) + foo<double>(11.22) / foo<double>(66.77);
+  double R4 = foo<double, int>(34.56, 1234);
+  double R5 = R1 + R2 * R3 - R4 + one[0] * foo<double>(15.52) - two[1] / foo<double>(51.25);
+  return R5 * R1 + R4 / R3 + R2;
 }
 }
 
@@ -150,7 +162,10 @@
 };
 _Wrap_alloc<int>::rebind<int> w;
 
-// CHECK: Miscellaneous Ungrouped Timers
+// FIXME:  We need more complex test to increase the compilation time;
+// otherwise we see the foolowing message from time to time only.
+// VIOLATILE-CHECK: Clang Timers: CodeGen Functions
+// CHECK-DAG: Miscellaneous Ungrouped Timers
 // CHECK-DAG: LLVM IR Generation Time
 // CHECK-DAG: Code Generation Time
 // CHECK: Total
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -27,6 +27,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtOpenMP.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Sema/Designator.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Ownership.h"
@@ -11002,6 +11003,14 @@
 
   LSI->CallOperator = NewCallOperator;
 
+  if (FrontendTimesIsEnabled) {
+    // At this point we're sure we're dealing with some function that's why
+    // we're starting the corresponding time slice. We'll stop it in
+    // Sema::ActOnFinishFunctionBody.
+    getFrontendFunctionTimeCtx<const FunctionDecl *>()->startFrontendTimer(
+        {NewCallOperator, 0.0});
+  }
+
   for (unsigned I = 0, NumParams = NewCallOperator->getNumParams();
        I != NumParams; ++I) {
     auto *P = NewCallOperator->getParamDecl(I);
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp
+++ lib/Sema/SemaLambda.cpp
@@ -10,17 +10,18 @@
 //  This file implements semantic analysis for C++ lambda expressions.
 //
 //===----------------------------------------------------------------------===//
-#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/SemaLambda.h"
 #include "TypeLocBuilder.h"
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaInternal.h"
-#include "clang/Sema/SemaLambda.h"
 using namespace clang;
 using namespace sema;
 
@@ -1437,6 +1438,14 @@
 ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, 
                                  Scope *CurScope) {
   LambdaScopeInfo LSI = *cast<LambdaScopeInfo>(FunctionScopes.back());
+
+  if (FrontendTimesIsEnabled) {
+    // We're dealing with lambda-function that's why we're starting
+    // the corresponding time slice. It will be finished in
+    // ActOnFinishFunctionBody.
+    getFrontendFunctionTimeCtx<const FunctionDecl *>()->startFrontendTimer(
+        {LSI.CallOperator, 0.0});
+  }
   ActOnFinishFunctionBody(LSI.CallOperator, Body);
   return BuildLambdaExpr(StartLoc, Body->getLocEnd(), &LSI);
 }
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -28,6 +28,7 @@
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Lex/HeaderSearch.h" // TODO: Sema shouldn't depend on Lex
 #include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering.
 #include "clang/Lex/ModuleLoader.h" // TODO: Sema shouldn't depend on Lex
@@ -8278,6 +8279,10 @@
                                               isVirtualOkay);
   if (!NewFD) return nullptr;
 
+  FrontendTimeRAII<const FunctionDecl *> FTRAII(
+      FrontendTimesIsEnabled,
+      getFrontendFunctionTimeCtx<const FunctionDecl *>(), {NewFD, 0});
+
   if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer())
     NewFD->setTopLevelDeclInObjCContainer();
 
@@ -12420,6 +12425,13 @@
   else
     FD = cast<FunctionDecl>(D);
 
+  if (FrontendTimesIsEnabled) {
+    // We're starting with new function definition that's why we're starting
+    // the new time slice. It will be stopped in ActOnFinishFunctionBody.
+    getFrontendFunctionTimeCtx<const FunctionDecl *>()->startFrontendTimer(
+        {FD, 0.0});
+  }
+
   // Check for defining attributes before the check for redefinition.
   if (const auto *Attr = FD->getAttr<AliasAttr>()) {
     Diag(Attr->getLocation(), diag::err_alias_is_definition) << FD << 0;
@@ -12925,6 +12937,19 @@
     DiscardCleanupsInEvaluationContext();
   }
 
+  if (FrontendTimesIsEnabled) {
+    assert(getFrontendFunctionTimeCtx<const FunctionDecl *>()
+                   ->ChildStack.back()
+                   .first == FD &&
+           "Invalid FD");
+    // We're stopping the current time slice and adding the one to FrontendTimes.
+    //  This slice was started in one of the following places:
+    //  TreeTransform<Derived>::TransformLambdaExpr
+    //  Sema::ActOnLambdaExpr
+    //  Sema::ActOnStartOfFunctionDef
+    getFrontendFunctionTimeCtx<const FunctionDecl *>()->stopFrontendTimer();
+  }
+
   return dcl;
 }
 
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -13,27 +13,63 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 using namespace clang;
 
+#define DEBUG_TYPE "parsetemplate"
+
 /// Parse a template declaration, explicit instantiation, or
 /// explicit specialization.
 Decl *Parser::ParseDeclarationStartingWithTemplate(
     DeclaratorContext Context, SourceLocation &DeclEnd,
     ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
   ObjCDeclContextSwitch ObjCDC(*this);
-  
+  Decl *Result;
+
+  if (FrontendTimesIsEnabled) {
+    LLVM_DEBUG(getFrontendFunctionTimeCtx<const FunctionDecl *>()->debugPrint(
+        "ParseDeclarationStartingWithTemplate: ", nullptr));
+    // At this moment we don't know if this template is interesting for time
+    // report but we have to start the timer if it is. The decision will be
+    // done below after instatiation/specialization.
+    getFrontendFunctionTimeCtx<const FunctionDecl *>()->startFrontendTimer(
+        {nullptr, 0.0});
+  }
   if (Tok.is(tok::kw_template) && NextToken().isNot(tok::less)) {
-    return ParseExplicitInstantiation(Context, SourceLocation(), ConsumeToken(),
-                                      DeclEnd, AccessAttrs, AS);
+    Result = ParseExplicitInstantiation(
+        Context, SourceLocation(), ConsumeToken(), DeclEnd, AccessAttrs, AS);
+  } else
+    Result = ParseTemplateDeclarationOrSpecialization(Context, DeclEnd,
+                                                      AccessAttrs, AS);
+  if (FrontendTimesIsEnabled) {
+    bool Done = false;
+    if (const auto *F = dyn_cast_or_null<FunctionDecl>(Result)) {
+      if (F->isFunctionOrFunctionTemplate() && F->hasBody()) {
+        LLVM_DEBUG(
+            getFrontendFunctionTimeCtx<const FunctionDecl *>()->debugPrint(
+                "stopFrontendTimer(ParseDeclarationStartingWithTemplate): ",
+                F));
+        // Yes, we should add this time slice to the given function
+        getFrontendFunctionTimeCtx<const FunctionDecl *>()->stopFrontendTimer(
+            true, {F, 0.0});
+        Done = true;
+      }
+    }
+    if (!Done) {
+      // We should not add this time slice to any function
+      getFrontendFunctionTimeCtx<const FunctionDecl *>()->stopFrontendTimer(
+          true, {nullptr, -1.0});
+      LLVM_DEBUG(llvm::dbgs() << "ParseDeclarationStartingWithTemplate: simply "
+                                 "remove the non-func time slice from times\n");
+    }
   }
-  return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AccessAttrs,
-                                                  AS);
+  return Result;
 }
 
 /// Parse a template declaration or an explicit specialization.
Index: lib/Parse/CMakeLists.txt
===================================================================
--- lib/Parse/CMakeLists.txt
+++ lib/Parse/CMakeLists.txt
@@ -24,6 +24,7 @@
   LINK_LIBS
   clangAST
   clangBasic
+  clangFrontend
   clangLex
   clangSema
   )
Index: lib/Frontend/FrontendTiming.cpp
===================================================================
--- lib/Frontend/FrontendTiming.cpp
+++ lib/Frontend/FrontendTiming.cpp
@@ -7,14 +7,42 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file keps implementation of frontend timing utils.
+// This file keeps implementation of frontend timing utils.
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/Decl.h"
 #include "clang/Frontend/Utils.h"
+#include "llvm/ADT/StringRef.h"
 
 namespace clang {
 
 bool FrontendTimesIsEnabled = false;
+llvm::TimerGroup *FDefTimeGroup = nullptr;
 
+using FTimeBase = const FunctionDecl *;
+FrontendTimeCtx<FTimeBase> FuncTimeCtx;
+
+template <>
+FrontendTimeCtx<FTimeBase> *getFrontendFunctionTimeCtx<FTimeBase>() {
+  if (FrontendTimesIsEnabled && !FuncTimeCtx.isValid())
+    FuncTimeCtx.init("cftimer", "Clang Function Timer",
+                     FuncTimeCtx.getFrontendDefaultTimerGroup());
+  return &FuncTimeCtx;
+}
+
+template <> bool isFirstValid<FTimeBase>(FTimeBase First) {
+  assert(First && "Invalid First");
+  if (FrontendTimesIsEnabled && FuncTimeCtx.isValid() &&
+      !First->isInvalidDecl() && First->getIdentifier()) {
+    if (First->getVisibility() == DefaultVisibility && First->hasBody())
+      return true;
+  }
+  return false;
+}
+
+template <> bool isFirstValid<llvm::StringRef>(llvm::StringRef First) {
+  assert(!First.empty() && "Invalid First");
+  return FrontendTimesIsEnabled;
+}
 }
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -783,6 +783,10 @@
   Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier);
 
   Opts.ControlFlowGuard = Args.hasArg(OPT_cfguard);
+  if (!Args.getLastArgValue(OPT_ftime_report_threshold, "0.04")
+           .getAsDouble(Opts.FTimeReportThreshold)) {
+    // TODO: Report a error message
+  }
 
   Opts.DisableGCov = Args.hasArg(OPT_test_coverage);
   Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data);
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -43,6 +43,7 @@
 #include "clang/Basic/Version.h"
 #include "clang/CodeGen/ConstantInitBuilder.h"
 #include "clang/Frontend/CodeGenOptions.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Sema/SemaDiagnostic.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
@@ -4449,13 +4450,17 @@
   switch (D->getKind()) {
   case Decl::CXXConversion:
   case Decl::CXXMethod:
-  case Decl::Function:
+  case Decl::Function: {
+    FrontendTimeRAII<const FunctionDecl *> FTRAII(
+        FrontendTimesIsEnabled,
+        getFrontendFunctionTimeCtx<const FunctionDecl *>(),
+        {cast<FunctionDecl>(D), 0});
     EmitGlobal(cast<FunctionDecl>(D));
     // Always provide some coverage mapping
     // even for the functions that aren't emitted.
     AddDeferredUnusedCoverageMapping(D);
     break;
-
+  }
   case Decl::CXXDeductionGuide:
     // Function-like, but does not result in code emission.
     break;
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -13,9 +13,9 @@
 
 #include "CodeGenFunction.h"
 #include "CGBlocks.h"
-#include "CGCleanup.h"
 #include "CGCUDARuntime.h"
 #include "CGCXXABI.h"
+#include "CGCleanup.h"
 #include "CGDebugInfo.h"
 #include "CGOpenMPRuntime.h"
 #include "CodeGenModule.h"
@@ -31,6 +31,7 @@
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
 #include "clang/Frontend/CodeGenOptions.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Sema/SemaDiagnostic.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Dominators.h"
@@ -93,6 +94,8 @@
   Builder.setFastMathFlags(FMF);
 }
 
+using FTimeBase = const FunctionDecl *;
+
 CodeGenFunction::~CodeGenFunction() {
   assert(LifetimeExtendedCleanupStack.empty() && "failed to emit a cleanup");
 
@@ -104,6 +107,13 @@
 
   if (getLangOpts().OpenMP && CurFn)
     CGM.getOpenMPRuntime().functionFinished(*this);
+  if (FrontendTimesIsEnabled && CurFuncDecl) {
+    // We're completing with the current function and as result we should
+    // add the current time slice to the common compilation time of the given
+    // function. This time slice was started in CodeGenFunction::StartFunction
+    // and finished here.
+    getFrontendFunctionTimeCtx<FTimeBase>()->stopFrontendTimer();
+  }
 }
 
 CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T,
@@ -820,9 +830,17 @@
 
   DidCallStackSave = false;
   CurCodeDecl = D;
-  if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
+  if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) {
     if (FD->usesSEHTry())
       CurSEHParent = FD;
+    if (FrontendTimesIsEnabled) {
+      // We're dealing with function that's wy we should add this time slice
+      // to the common compilation time of the given function. The end of this
+      // time slice will be fixed in destructor
+      // CodeGenFunction::~CodeGenFunction().
+      getFrontendFunctionTimeCtx<FTimeBase>()->startFrontendTimer({FD, 0.0});
+    }
+  }
   CurFuncDecl = (D ? D->getNonClosureContext() : nullptr);
   FnRetTy = RetTy;
   CurFn = Fn;
Index: lib/CodeGen/CodeGenAction.cpp
===================================================================
--- lib/CodeGen/CodeGenAction.cpp
+++ lib/CodeGen/CodeGenAction.cpp
@@ -22,7 +22,9 @@
 #include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
 #include "llvm/IR/DebugInfo.h"
@@ -36,15 +38,17 @@
 #include "llvm/Pass.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
-#include "llvm/Support/Timer.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/YAMLTraits.h"
 #include "llvm/Transforms/IPO/Internalize.h"
 
 #include <memory>
 using namespace clang;
 using namespace llvm;
 
+using FTimeBase = const FunctionDecl *;
+using FTP = std::pair<FTimeBase, double>;
+
 namespace clang {
   class BackendConsumer;
   class ClangDiagnosticHandler final : public DiagnosticHandler {
@@ -107,6 +111,18 @@
     // refers to.
     llvm::Module *CurLinkModule = nullptr;
 
+    using FTimeMark = StringRef;
+    using FTP = std::pair<FTimeMark, double>;
+    using FTPIterator = std::vector<FTP>::iterator;
+
+    class BCSortClassName {
+    public:
+      bool operator()(FTP i, FTP j) { return i.first.compare(j.first) < 0; }
+      StringRef getName(FTP E) { return E.first; }
+    };
+
+    FrontendTimeCtx<FTimeMark> BCTimerCtx;
+
   public:
     BackendConsumer(BackendAction Action, DiagnosticsEngine &Diags,
                     const HeaderSearchOptions &HeaderSearchOpts,
@@ -128,6 +144,12 @@
           LinkModules(std::move(LinkModules)) {
       FrontendTimesIsEnabled = TimePasses;
     }
+
+    ~BackendConsumer() {
+      if (FrontendTimesIsEnabled)
+        BCTimerCtx.print<BCSortClassName>(BCSortClassName(), "BackendConsumer");
+    }
+
     llvm::Module *getModule() const { return Gen->GetModule(); }
     std::unique_ptr<llvm::Module> takeModule() {
       return std::unique_ptr<llvm::Module>(Gen->ReleaseModule());
@@ -177,6 +199,9 @@
     }
 
     void HandleInlineFunctionDefinition(FunctionDecl *D) override {
+      FrontendTimeRAII<FTimeBase> FTRAII(
+          FrontendTimesIsEnabled,
+          getFrontendFunctionTimeCtx<FTimeBase>(), {D, 0});
       PrettyStackTraceDecl CrashInfo(D, SourceLocation(),
                                      Context->getSourceManager(),
                                      "LLVM IR generation of inline function");
@@ -795,7 +820,13 @@
 
 CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext)
     : Act(_Act), VMContext(_VMContext ? _VMContext : new LLVMContext),
-      OwnsVMContext(!_VMContext) {}
+      OwnsVMContext(!_VMContext) {
+  if (FrontendTimesIsEnabled) {
+    CompilerInstance &CI = getCompilerInstance();
+    getFrontendFunctionTimeCtx<FTimeBase>()->setThreshold(
+        CI.getCodeGenOpts().FTimeReportThreshold);
+  }
+}
 
 CodeGenAction::~CodeGenAction() {
   TheModule.reset();
@@ -805,11 +836,34 @@
 
 bool CodeGenAction::hasIRSupport() const { return true; }
 
+static StringRef getFDName(CodeGen::CodeGenModule &CGM,
+                           const FunctionDecl *FD) {
+  assert(isFirstValid<const FunctionDecl *>(FD) && "Invalid FD");
+  return CGM.getMangledName(GlobalDecl(FD));
+}
+
+class CGSortClassName {
+  CodeGen::CodeGenModule &CGM;
+
+public:
+  CGSortClassName(CodeGen::CodeGenModule &_CGM) : CGM(_CGM) {}
+  bool operator()(FTP i, FTP j) {
+    StringRef NameI = getFDName(CGM, i.first);
+    StringRef NameJ = getFDName(CGM, j.first);
+    return NameI.compare(NameJ) < 0;
+  }
+
+  StringRef getName(FTP E) { return getFDName(CGM, E.first); }
+};
+
 void CodeGenAction::EndSourceFileAction() {
   // If the consumer creation failed, do nothing.
   if (!getCompilerInstance().hasASTConsumer())
     return;
-
+  if (FrontendTimesIsEnabled)
+    getFrontendFunctionTimeCtx<FTimeBase>()->print<CGSortClassName>(
+        CGSortClassName(BEConsumer->getCodeGenerator()->CGM()),
+        "CodeGen Functions", " (*)");
   // Steal the module from the consumer.
   TheModule = BEConsumer->takeModule();
 }
Index: include/clang/Frontend/Utils.h
===================================================================
--- include/clang/Frontend/Utils.h
+++ include/clang/Frontend/Utils.h
@@ -17,12 +17,15 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/VirtualFileSystem.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Option/OptSpecifier.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Timer.h"
 #include <cstdint>
 #include <memory>
 #include <string>
@@ -239,7 +242,246 @@
 /// If the user specifies the -ftime-report argument on an Clang command line
 /// then the value of this boolean will be true, otherwise false.
 extern bool FrontendTimesIsEnabled;
+extern llvm::TimerGroup *FDefTimeGroup;
+
+template <typename T> bool isFirstValid(T First);
+
+template <typename T> struct FrontendTimeCtx {
+  using FTimePair = std::pair<T, double>;
+  std::vector<FTimePair> FrontendTimes;
+  llvm::Timer FrontendTimer;
+  llvm::TimerGroup *TGroup = nullptr;
+  bool DeleteTGroup = false;
+  double ChildTime;
+  double Threshold = 0.0;
+  std::vector<FTimePair> ChildStack;
+  bool IsValid = false;
+
+  static double getCurProcessTime(llvm::Timer &FT) {
+    assert(FT.isRunning() && "FrontendTimer must be running");
+    FT.stopTimer();
+    return FT.getTotalTime().getProcessTime();
+  }
+
+  static llvm::TimerGroup *getFrontendDefaultTimerGroup() {
+    if (!FDefTimeGroup)
+      FDefTimeGroup = (llvm::TimerGroup *)new llvm::TimerGroup(
+          "clangdeftg", "Clang Timers Group");
+    return FDefTimeGroup;
+  }
+
+public:
+  FrontendTimeCtx() : IsValid(false){};
+  bool isValid(){return IsValid;}
+  void setThreshold(double _T){Threshold = _T;}
+  void init(llvm::StringRef TimerName, llvm::StringRef TimerDsc,
+            llvm::StringRef GroupName, llvm::StringRef GroupDsc) {
+    if (FrontendTimesIsEnabled) {
+      TGroup = (llvm::TimerGroup *)new llvm::TimerGroup(GroupName, GroupDsc);
+      DeleteTGroup = true;
+      init(TimerName, TimerDsc, TGroup);
+    } else
+      IsValid = false;
+  };
+
+  void init(llvm::StringRef TimerName, llvm::StringRef TimerDsc,
+            llvm::TimerGroup *TG) {
+    if (FrontendTimesIsEnabled) {
+      FrontendTimer.init(TimerName, TimerDsc, *TG);
+      TGroup = TG;
+      ChildStack.clear();
+      ChildTime = 0.0;
+      IsValid = true;
+    } else
+      IsValid = false;
+  };
+
+  ~FrontendTimeCtx() {
+    if (FrontendTimesIsEnabled && IsValid && DeleteTGroup)
+      delete TGroup;
+  }
+
+  void startFrontendTimer(FTimePair TDsc) {
+    if (FrontendTimesIsEnabled) {
+      assert(IsValid && "Time Context must be initialized");
+      if (FrontendTimer.isRunning())
+        // It stops the timer as a side effect
+        TDsc.second = getCurProcessTime(FrontendTimer);
+      else
+        TDsc.second = FrontendTimer.getTotalTime().getProcessTime();
+      ChildStack.push_back(TDsc);
+      FrontendTimer.startTimer();
+    }
+  }
+
+  bool stopFrontendTimer(bool UseEl = false, FTimePair El = FTimePair()) {
+    if (FrontendTimesIsEnabled) {
+      assert(IsValid && "Time Context must be initialized");
+      assert(FrontendTimer.isRunning() && "FrontendTimer must be running");
+      assert(!ChildStack.empty() &&
+             "There should be at least one running time slice");
+      FTimePair Result = ChildStack.back();
+      ChildStack.pop_back();
+      // As side effect getCurProcessTime does stopTimer().
+      // Should we fix such a side effect?
+      double CurProcTime = getCurProcessTime(FrontendTimer) - Result.second;
+      Result.second = CurProcTime - ChildTime;
+      if (ChildStack.empty())
+        ChildTime = 0;
+      else {
+        ChildTime += Result.second;
+        FrontendTimer.startTimer();
+      }
+      if (UseEl) {
+        if (El.second > 0)
+          Result.first = El.first;
+        else
+          return false;
+      }
+      if (isFirstValid(Result.first) && Result.second > Threshold) {
+        FrontendTimes.push_back(Result);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  using FTimePair2 = std::pair<FTimePair, unsigned>;
+  static bool ftimeSort2(FTimePair2 I, FTimePair2 J) {
+    return I.first.second < J.first.second;
+  }
+
+  static bool ftimeSort(FTimePair I, FTimePair J) {
+    return I.second < J.second;
+  }
+
+  llvm::TimerGroup *getTimerGroup() {
+    assert(IsValid && "Time Context must be initialized");
+    return TGroup;
+  }
 
+  llvm::Timer *getFrontendTimer() { return &FrontendTimer; }
+
+  std::vector<FTimePair> *getFrontendTimes() { return &FrontendTimes; }
+
+  // Print report about function compilation times
+  // SortName - a functor to sort times by names
+  // SubGroup - a times delimeter to include in output header
+  // Mark - included in the output to simplify grepping of the log
+  template <typename Compare>
+  void print(Compare SortName, StringRef SubGroup, StringRef Mark = " (+)") {
+    if (FrontendTimesIsEnabled && !FrontendTimes.empty()) {
+      std::unique_ptr<llvm::raw_ostream> OutStream =
+          llvm::CreateInfoOutputFile();
+      // FromtendTimes keep time slices spent on the given funcs during
+      // compilation. There could be several time slices corresponding to
+      // on function: parsing time, llvm generation time, code generation time,
+      // etc. We combine such time slices in one time slot related to one func.
+      llvm::sort(FrontendTimes.begin(), FrontendTimes.end(), SortName);
+      using FTPIterator = typename std::vector<FTimePair>::iterator;
+      std::pair<FTPIterator, FTPIterator> range;
+
+      // FinalTimes keep values of func compilation times
+      std::vector<FTimePair2> FinalTimes;
+
+      *OutStream << "===------------ Clang Timers: " << SubGroup
+                 << " ------------==\n";
+      for (unsigned i = 0; i < FrontendTimes.size();) {
+        range = std::equal_range(FrontendTimes.begin() + i, FrontendTimes.end(),
+                                 FrontendTimes[i], SortName);
+        auto dist = std::distance(range.first, range.second);
+        FTimePair E = {FrontendTimes[i].first, 0};
+        // If we have several time slices related to one func than we sum all
+        // corresponding time values to get time slot of one function
+        while (range.first != range.second) {
+          E.second += range.first->second;
+          range.first++;
+        }
+        FinalTimes.push_back({E, dist});
+        i += dist;
+      }
+      // We sort FinalTimes to find the longest compilation times
+      llvm::sort(FinalTimes.begin(), FinalTimes.end(), ftimeSort2);
+      // TODO: TimeThreshold is used to include the compilation time in output.
+      // Should we use special switch here? The smaller TimeThreshold the more
+      // output we generate.
+      double TimeThreshold =
+          (FinalTimes.front().first.second + FinalTimes.back().first.second) /
+          2;
+      for (auto E : FinalTimes) {
+        // FIXME: do we need second threshold here?
+        if ((E.first.second > TimeThreshold) || (E.second > 1))
+          *OutStream << llvm::format("%7.4f (%d) ", E.second, E.first.second)
+                     << SortName.getName(E.first) << Mark << "\n";
+      }
+    }
+  }
+  void debugPrint(StringRef H, const void *P) {
+    llvm::dbgs() << H << P
+                 << llvm::format("FrontendTimes.size=%d,ChildStack.size=%d,"
+                                 "ChildTime=%7.4f, ProcessTime=%7.4f\n",
+                                 FrontendTimes.size(), ChildStack.size(),
+                                 ChildTime,
+                                 FrontendTimer.getTotalTime().getProcessTime());
+  }
+};
+
+template <typename T> FrontendTimeCtx<T> *getFrontendFunctionTimeCtx();
+
+template <typename T> class FrontendTimeRAII {
+  using FTimePair = std::pair<T, double>;
+  FrontendTimeCtx<T> *Ctx = nullptr;
+  void init(FTimePair E, llvm::StringRef TName, llvm::StringRef TDsc,
+            llvm::StringRef GName, llvm::StringRef GDsc) {
+    if (Ctx) {
+      if (!Ctx->IsValid)
+        Ctx->init(TName, TDsc, GName, GDsc);
+      Ctx->startFrontendTimer(E);
+    }
+  }
+
+  void init(FTimePair E, llvm::StringRef TName, llvm::StringRef TDsc,
+            llvm::TimerGroup *TG) {
+    if (Ctx) {
+      if (!Ctx->IsValid)
+        Ctx->init(TName, TDsc, TG);
+      Ctx->startFrontendTimer(E);
+    }
+  }
+
+public:
+  FrontendTimeRAII(bool Enabled, FrontendTimeCtx<T> *FTC, FTimePair E,
+                   llvm::StringRef TName, llvm::StringRef TDsc,
+                   llvm::TimerGroup *TG) {
+    if (Enabled) {
+      Ctx = FTC;
+      init(E, TName, TDsc);
+    }
+  }
+
+  FrontendTimeRAII(bool Enabled, FrontendTimeCtx<T> *FTC, FTimePair E,
+                   llvm::StringRef TName, llvm::StringRef TDsc,
+                   llvm::StringRef GName, llvm::StringRef GDsc) {
+    if (Enabled) {
+      Ctx = FTC;
+      init(E, TName, TDsc, GName, GDsc);
+    }
+  }
+
+  FrontendTimeRAII(bool Enabled, FrontendTimeCtx<T> *FTC, FTimePair E) {
+    if (Enabled) {
+      Ctx = FTC;
+      init(E, "clangtimer", "Clang Func Timer",
+           FrontendTimeCtx<T>::getFrontendDefaultTimerGroup());
+    }
+  }
+
+  ~FrontendTimeRAII() {
+    if (Ctx && Ctx->IsValid) {
+      Ctx->stopFrontendTimer();
+    }
+  }
+};
 } // namespace clang
 
 #endif // LLVM_CLANG_FRONTEND_UTILS_H
Index: include/clang/Frontend/CodeGenOptions.h
===================================================================
--- include/clang/Frontend/CodeGenOptions.h
+++ include/clang/Frontend/CodeGenOptions.h
@@ -116,6 +116,9 @@
   /// environment variables.
   std::string CoverageDataFile;
 
+  /// Compilation time threshold to be included in funcs time report
+  double FTimeReportThreshold;
+
   /// The filename with path we use for coverage notes files.
   std::string CoverageNotesFile;
 
Index: include/clang/Driver/CC1Options.td
===================================================================
--- include/clang/Driver/CC1Options.td
+++ include/clang/Driver/CC1Options.td
@@ -226,6 +226,10 @@
   HelpText<"Emit coverage data to this filename.">;
 def coverage_data_file_EQ : Joined<["-"], "coverage-data-file=">,
   Alias<coverage_data_file>;
+def ftime_report_threshold : Separate<["-"], "ftime-report-threshold">,
+  HelpText<"Emit coverage data to this filename.">;
+def ftime_report_threshold_EQ : Joined<["-"], "ftime-report-threshold=">,
+  Alias<ftime_report_threshold>;
 def coverage_notes_file : Separate<["-"], "coverage-notes-file">,
   HelpText<"Emit coverage notes to this filename.">;
 def coverage_notes_file_EQ : Joined<["-"], "coverage-notes-file=">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to