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