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 @@ -30,6 +33,10 @@ #include #include +#ifndef DEBUG_TYPE +#define DEBUG_TYPE "ftiming" +#endif + namespace llvm { class Triple; @@ -239,7 +246,272 @@ /// 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 llvm::StringRef getFirstName(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, StringRef Tag) { + 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); + LLVM_DEBUG(auto N = getFirstName(TDsc.first); + if (!N.empty()) debugPrint(Tag, "start", N, TDsc.second)); + FrontendTimer.startTimer(); + } + } + + bool stopFrontendTimer(StringRef Tag, 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); + LLVM_DEBUG(auto N = getFirstName(Result.first); if (!N.empty()) + debugPrint(Tag, "stop-add", N, Result.second)); + return true; + } else if (Result.second <= Threshold) { + LLVM_DEBUG(auto N = getFirstName(Result.first); if (!N.empty()) + debugPrint(Tag, "stop-zero", N, Result.second)); + } + } + 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 Tag, StringRef Head, StringRef Name, + double Duration) { + llvm::dbgs() << Tag << ":" << Head << ":" << Name << ":" + << llvm::format("%7.4f ", Duration) + << llvm::format("FrontendTimes.size=%d,ChildStack.size=%d," + "ChildTime=%7.4f, ProcessTime=%7.4f\n", + FrontendTimes.size(), ChildStack.size(), + ChildTime, + FrontendTimer.getTotalTime().getProcessTime()); + } + + 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; + StringRef Tag; + 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, Tag); + } + } + + 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, Tag); + } + } + +public: + FrontendTimeRAII(StringRef Tg, bool Enabled, FrontendTimeCtx *FTC, + FTimePair E, llvm::StringRef TName, llvm::StringRef TDsc, + llvm::TimerGroup *TG) + : Tag(Tg) { + if (Enabled) { + Ctx = FTC; + init(Tag, E, TName, TDsc); + } + } + + FrontendTimeRAII(StringRef Tg, bool Enabled, FrontendTimeCtx *FTC, + FTimePair E, llvm::StringRef TName, llvm::StringRef TDsc, + llvm::StringRef GName, llvm::StringRef GDsc) + : Tag(Tg) { + if (Enabled) { + Ctx = FTC; + init(Tag, E, TName, TDsc, GName, GDsc); + } + } + + FrontendTimeRAII(StringRef Tg, bool Enabled, FrontendTimeCtx *FTC, + FTimePair E) + : Tag(Tg) { + if (Enabled) { + Ctx = FTC; + init(E, "clangtimer", "Clang Func Timer", + FrontendTimeCtx::getFrontendDefaultTimerGroup()); + } + } + + ~FrontendTimeRAII() { + if (Ctx && Ctx->IsValid) { + Ctx->stopFrontendTimer(Tag); + } + } +}; } // namespace clang #endif // LLVM_CLANG_FRONTEND_UTILS_H Index: lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- lib/Analysis/AnalysisDeclContext.cpp +++ lib/Analysis/AnalysisDeclContext.cpp @@ -35,6 +35,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Frontend/Utils.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/STLExtras.h" Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -27,6 +27,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/NSAPI.h" #include "clang/Frontend/CodeGenOptions.h" +#include "clang/Frontend/Utils.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/DataLayout.h" @@ -2569,8 +2570,12 @@ return LV; } - if (const auto *FD = dyn_cast(ND)) + if (const auto *FD = dyn_cast(ND)) { + FrontendTimeRAII FTRAII( + "EmitDeclRefLValue", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {FD, 0}); return EmitFunctionDeclLValue(*this, E, FD); + } // FIXME: While we're emitting a binding from an enclosing scope, all other // DeclRefExprs we see should be implicitly treated as if they also refer to @@ -3773,8 +3778,12 @@ return LV; } - if (const auto *FD = dyn_cast(ND)) + if (const auto *FD = dyn_cast(ND)) { + FrontendTimeRAII FTRAII( + "EmitMemberExpr", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {FD, 0}); return EmitFunctionDeclLValue(*this, E, FD); + } llvm_unreachable("Unhandled member declaration!"); } 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, @@ -129,6 +145,12 @@ FrontendTimesIsEnabled = TimePasses; llvm::TimePassesIsEnabled = 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()); @@ -178,6 +200,9 @@ } void HandleInlineFunctionDefinition(FunctionDecl *D) override { + FrontendTimeRAII FTRAII( + "HandleInlineFunctionDefinition", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {D, 0}); PrettyStackTraceDecl CrashInfo(D, SourceLocation(), Context->getSourceManager(), "LLVM IR generation of inline function"); @@ -796,7 +821,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(); @@ -806,11 +837,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,14 @@ 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( + "~CodeGenFunction"); + } } CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T, @@ -822,9 +833,18 @@ 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}, "StartFunction"); + } + } 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" @@ -4567,13 +4568,17 @@ switch (D->getKind()) { case Decl::CXXConversion: case Decl::CXXMethod: - case Decl::Function: + case Decl::Function: { + FrontendTimeRAII FTRAII( + "EmitTopLevelDecl", 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/CodeGen/ObjectFilePCHContainerOperations.cpp =================================================================== --- lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "pchcontainer" + #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "CGDebugInfo.h" #include "CodeGenModule.h" @@ -38,8 +40,6 @@ using namespace clang; -#define DEBUG_TYPE "pchcontainer" - namespace { class PCHContainerGenerator : public ASTConsumer { DiagnosticsEngine &Diags; Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -787,6 +787,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,58 @@ // //===----------------------------------------------------------------------===// // -// This file keps implementation of frontend timing utils. +// This file keeps implementation of frontend timing utils. // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ftiming" +#include "clang/AST/Decl.h" #include "clang/Frontend/Utils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Format.h" namespace clang { bool FrontendTimesIsEnabled = false; +llvm::TimerGroup *FDefTimeGroup = nullptr; +using FTimeBase = FunctionDecl const *; +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 <> llvm::StringRef getFirstName(FTimeBase First) { + if (FrontendTimesIsEnabled && FuncTimeCtx.isValid() && First && + !First->isInvalidDecl()) + return First->getNameAsString(); + return llvm::StringRef(); +} + +template <> bool isFirstValid(llvm::StringRef First) { + assert(!First.empty() && "Invalid First"); + return FrontendTimesIsEnabled; +} + +template <> +llvm::StringRef getFirstName(llvm::StringRef First) { + if (FrontendTimesIsEnabled && !First.empty()) + return First; + return llvm::StringRef(); +} } 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/ParseCXXInlineMethods.cpp =================================================================== --- lib/Parse/ParseCXXInlineMethods.cpp +++ lib/Parse/ParseCXXInlineMethods.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.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/Scope.h" 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" @@ -27,13 +28,38 @@ DeclaratorContext Context, SourceLocation &DeclEnd, ParsedAttributes &AccessAttrs, AccessSpecifier AS) { ObjCDeclContextSwitch ObjCDC(*this); + Decl *Result; + if (FrontendTimesIsEnabled) { + // 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}, "ParseDeclarationStartingWithTemplate"); + } 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()) { + // Yes, we should add this time slice to the given function + getFrontendFunctionTimeCtx()->stopFrontendTimer( + "ParseDeclarationStartingWithTemplate", true, {F, 0.0}); + Done = true; + } + } + if (!Done) { + // We should not add this time slice to any function + getFrontendFunctionTimeCtx()->stopFrontendTimer( + "", true, {nullptr, -1.0}); + } } - 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 @@ -1990,6 +1991,9 @@ SC_Extern, false, R->isFunctionProtoType()); + FrontendTimeRAII FTRAII( + "LazilyCreateBuiltin", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {New, 0}); New->setImplicit(); // Create Decl objects for each parameter, adding them to the @@ -8345,6 +8349,10 @@ isVirtualOkay); if (!NewFD) return nullptr; + FrontendTimeRAII FTRAII( + "ActOnFunctionDeclarator", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {NewFD, 0}); + if (OriginalLexicalContext && OriginalLexicalContext->isObjCContainer()) NewFD->setTopLevelDeclInObjCContainer(); @@ -9866,6 +9874,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, LookupResult &Previous, bool IsMemberSpecialization) { + FrontendTimeRAII FTRAII( + "CheckFunctionDeclaration", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {NewFD, 0}); assert(!NewFD->getReturnType()->isVariablyModifiedType() && "Variably modified return types are not handled here"); @@ -12754,6 +12765,10 @@ else FD = cast(D); + FrontendTimeRAII FTRAII( + "ActOnStartOfFunctionDef", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {FD, 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; @@ -12965,6 +12980,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation) { FunctionDecl *FD = dcl ? dcl->getAsFunction() : nullptr; + FrontendTimeRAII FTRAII( + "ActOnFinishFunctionBody", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {FD, 0}); sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; @@ -13265,7 +13283,6 @@ if (getDiagnostics().hasErrorOccurred()) { DiscardCleanupsInEvaluationContext(); } - 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; @@ -906,6 +907,11 @@ CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, ParamInfo.getDeclSpec().isConstexprSpecified()); + + FrontendTimeRAII FTRAII( + "ActOnStartOfLambdaDefinition", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Method, 0}); + if (ExplicitParams) CheckCXXDefaultArguments(Method); @@ -1493,6 +1499,10 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, LambdaScopeInfo *LSI) { + FrontendTimeRAII FTRAII( + "BuildLambdaExpr", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), + {LSI->CallOperator, 0}); // Collect information from the lambda scope. SmallVector Captures; SmallVector CaptureInits; Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/Sema/Overload.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" @@ -23,8 +22,10 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/Utils.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" @@ -965,6 +966,9 @@ Sema::OverloadKind Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, NamedDecl *&Match, bool NewIsUsingDecl) { + FrontendTimeRAII FTRAII( + "CheckOverload", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {New, 0}); for (LookupResult::iterator I = Old.begin(), E = Old.end(); I != E; ++I) { NamedDecl *OldD = *I; @@ -1046,6 +1050,9 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs) { + FrontendTimeRAII FTRAII( + "IsOverload", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {New, 0}); // C++ [basic.start.main]p2: This function shall not be overloaded. if (New->isMain()) return false; @@ -5929,6 +5936,9 @@ bool PartialOverloading, bool AllowExplicit, ConversionSequenceList EarlyConversions) { + FrontendTimeRAII FTRAII( + "AddOverloadCandidate", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Function, 0}); const FunctionProtoType *Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -2968,6 +2968,9 @@ QualType *FunctionType, TemplateDeductionInfo &Info) { FunctionDecl *Function = FunctionTemplate->getTemplatedDecl(); + FrontendTimeRAII FTRAII( + "SubstituteExplicitTemplateArguments", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Function, 0}); TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); @@ -3801,6 +3804,9 @@ return TDK_Invalid; FunctionDecl *Function = FunctionTemplate->getTemplatedDecl(); + FrontendTimeRAII FTRAII( + "DeduceTemplateArguments", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Function, 0}); unsigned NumParams = Function->getNumParams(); unsigned FirstInnerIndex = getFirstInnerIndex(FunctionTemplate); @@ -4021,6 +4027,9 @@ return TDK_Invalid; FunctionDecl *Function = FunctionTemplate->getTemplatedDecl(); + FrontendTimeRAII FTRAII( + "DeduceTemplateArguments2", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Function, 0}); TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); QualType FunctionType = Function->getType(); Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -128,6 +128,9 @@ } // Add template arguments from a function template specialization. else if (FunctionDecl *Function = dyn_cast(Ctx)) { + FrontendTimeRAII FTRAII( + "getTemplateInstantiationArgs", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), {Function, 0}); if (!RelativeToPrimary && (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization && 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" @@ -10960,6 +10961,11 @@ LSI->CallOperator = NewCallOperator; + FrontendTimeRAII FTRAII( + "TransformLambdaExpr", FrontendTimesIsEnabled, + getFrontendFunctionTimeCtx(), + {NewCallOperator, 0.0}); + for (unsigned I = 0, NumParams = NewCallOperator->getNumParams(); I != NumParams; ++I) { auto *P = NewCallOperator->getParamDecl(I); Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "AnalysisConsumer" + #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "ModelInjector.h" #include "clang/AST/Decl.h" @@ -48,8 +50,6 @@ using namespace clang; using namespace ento; -#define DEBUG_TYPE "AnalysisConsumer" - static std::unique_ptr CreateUbiViz(); STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); Index: lib/Tooling/Tooling.cpp =================================================================== --- lib/Tooling/Tooling.cpp +++ lib/Tooling/Tooling.cpp @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "clang-tooling" + #include "clang/Tooling/Tooling.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticIDs.h" @@ -60,8 +62,6 @@ #include #include -#define DEBUG_TYPE "clang-tooling" - using namespace clang; using namespace tooling; 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