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; +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; def coverage_notes_file : Separate<["-"], "coverage-notes-file">, HelpText<"Emit coverage notes to this filename.">; def coverage_notes_file_EQ : Joined<["-"], "coverage-notes-file=">, 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/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 #include #include @@ -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 bool isFirstValid(T First); + +template struct FrontendTimeCtx { + using FTimePair = std::pair; + std::vector FrontendTimes; + llvm::Timer FrontendTimer; + llvm::TimerGroup *TGroup = nullptr; + bool DeleteTGroup = false; + double ChildTime; + double Threshold = 0.0; + std::vector 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; + 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 *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 + void print(Compare SortName, StringRef SubGroup, StringRef Mark = " (+)") { + if (FrontendTimesIsEnabled && !FrontendTimes.empty()) { + std::unique_ptr 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::iterator; + std::pair range; + + // FinalTimes keep values of func compilation times + std::vector 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 FrontendTimeCtx *getFrontendFunctionTimeCtx(); + +template class FrontendTimeRAII { + using FTimePair = std::pair; + FrontendTimeCtx *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 *FTC, FTimePair E, + llvm::StringRef TName, llvm::StringRef TDsc, + llvm::TimerGroup *TG) { + if (Enabled) { + Ctx = FTC; + init(E, TName, TDsc); + } + } + + FrontendTimeRAII(bool Enabled, FrontendTimeCtx *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 *FTC, FTimePair E) { + if (Enabled) { + Ctx = FTC; + init(E, "clangtimer", "Clang Func Timer", + FrontendTimeCtx::getFrontendDefaultTimerGroup()); + } + } + + ~FrontendTimeRAII() { + if (Ctx && Ctx->IsValid) { + Ctx->stopFrontendTimer(); + } + } +}; } // namespace clang #endif // LLVM_CLANG_FRONTEND_UTILS_H 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,7 +38,6 @@ #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" @@ -45,6 +46,9 @@ using namespace clang; using namespace llvm; +using FTimeBase = const FunctionDecl *; +using FTP = std::pair; + 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; + using FTPIterator = std::vector::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 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(), "BackendConsumer"); + } + llvm::Module *getModule() const { return Gen->GetModule(); } std::unique_ptr takeModule() { return std::unique_ptr(Gen->ReleaseModule()); @@ -177,6 +199,9 @@ } void HandleInlineFunctionDefinition(FunctionDecl *D) override { + FrontendTimeRAII FTRAII( + FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {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()->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(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()->print( + CGSortClassName(BEConsumer->getCodeGenerator()->CGM()), + "CodeGen Functions", " (*)"); // Steal the module from the consumer. TheModule = BEConsumer->takeModule(); } 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()->stopFrontendTimer(); + } } CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T, @@ -820,9 +830,17 @@ DidCallStackSave = false; CurCodeDecl = D; - if (const auto *FD = dyn_cast_or_null(D)) + if (const auto *FD = dyn_cast_or_null(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()->startFrontendTimer({FD, 0.0}); + } + } CurFuncDecl = (D ? D->getNonClosureContext() : nullptr); FnRetTy = RetTy; CurFn = Fn; 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 FTRAII( + FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), + {cast(D), 0}); EmitGlobal(cast(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/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/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 FuncTimeCtx; + +template <> +FrontendTimeCtx *getFrontendFunctionTimeCtx() { + if (FrontendTimesIsEnabled && !FuncTimeCtx.isValid()) + FuncTimeCtx.init("cftimer", "Clang Function Timer", + FuncTimeCtx.getFrontendDefaultTimerGroup()); + return &FuncTimeCtx; +} + +template <> bool isFirstValid(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 First) { + assert(!First.empty() && "Invalid First"); + return FrontendTimesIsEnabled; +} } 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/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -13,6 +13,7 @@ #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" @@ -21,19 +22,54 @@ #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()->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()->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(Result)) { + if (F->isFunctionOrFunctionTemplate() && F->hasBody()) { + LLVM_DEBUG( + getFrontendFunctionTimeCtx()->debugPrint( + "stopFrontendTimer(ParseDeclarationStartingWithTemplate): ", + F)); + // Yes, we should add this time slice to the given function + getFrontendFunctionTimeCtx()->stopFrontendTimer( + true, {F, 0.0}); + Done = true; + } + } + if (!Done) { + // We should not add this time slice to any function + getFrontendFunctionTimeCtx()->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/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 FTRAII( + FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {NewFD, 0}); + if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer()) NewFD->setTopLevelDeclInObjCContainer(); @@ -12420,6 +12425,13 @@ else FD = cast(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()->startFrontendTimer( + {FD, 0.0}); + } + // Check for defining attributes before the check for redefinition. if (const auto *Attr = FD->getAttr()) { Diag(Attr->getLocation(), diag::err_alias_is_definition) << FD << 0; @@ -12925,6 +12937,19 @@ DiscardCleanupsInEvaluationContext(); } + if (FrontendTimesIsEnabled) { + assert(getFrontendFunctionTimeCtx() + ->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::TransformLambdaExpr + // Sema::ActOnLambdaExpr + // Sema::ActOnStartOfFunctionDef + getFrontendFunctionTimeCtx()->stopFrontendTimer(); + } + return dcl; } 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(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()->startFrontendTimer( + {LSI.CallOperator, 0.0}); + } ActOnFinishFunctionBody(LSI.CallOperator, Body); return BuildLambdaExpr(StartLoc, Body->getLocEnd(), &LSI); } 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()->startFrontendTimer( + {NewCallOperator, 0.0}); + } + for (unsigned I = 0, NumParams = NewCallOperator->getNumParams(); I != NumParams; ++I) { auto *P = NewCallOperator->getParamDecl(I); 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 -void foo(); +T foo(T bar) { + T Result = bar * bar + bar / 1.2 + bar; + return Result; +}; template -void foo(); +T foo(T bar, U bar2) { + T Result = bar2 * bar + bar / 1.2 + bar2; + return Result; +}; // Template function definitions. template @@ -130,9 +136,15 @@ template oneT L<0>::O::Fun(U) { return one; } -void Instantiate() { +double Instantiate() { sassert(sizeof(L<0>::O::Fun(0)) == sizeof(one)); sassert(sizeof(L<0>::O::Fun(0)) == sizeof(one)); + int R1 = foo(123) + foo(177.2) - foo(331.442); + char R2 = foo('d', 1234) * foo(1.26); + int R3 = foo(1.2) + foo(11.22) / foo(66.77); + double R4 = foo(34.56, 1234); + double R5 = R1 + R2 * R3 - R4 + one[0] * foo(15.52) - two[1] / foo(51.25); + return R5 * R1 + R4 / R3 + R2; } } @@ -150,7 +162,10 @@ }; _Wrap_alloc::rebind 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: 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