[clang] [Frontend] Consolidate frontend timer setup in CompilerInstance::ExecuteAction. NFC (PR #192266)

Vassil Vassilev via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 11:34:15 PDT 2026


https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/192266

>From ecca0f7c64ca772a8e954268911e3c2b36de700a Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Wed, 15 Apr 2026 14:18:11 +0000
Subject: [PATCH] [Frontend] Consolidate frontend timer setup in
 CompilerInstance::ExecuteAction. NFC

Move the frontend timer creation (-ftime-report) and TimeTraceScope
("ExecuteCompiler") from cc1_main into CompilerInstance::ExecuteAction via a
new private PrepareForExecution() method. This ensures all tools that use
ExecuteAction (cc1, clang-repl, libclang, etc.) get consistent timing
infrastructure without duplicating setup code.
---
 .../include/clang/Frontend/CompilerInstance.h |  14 +++
 clang/lib/Frontend/CompilerInstance.cpp       |  20 ++++
 clang/test/Interpreter/ftime-report.cpp       |   5 +
 clang/tools/driver/cc1_main.cpp               |   9 +-
 clang/unittests/Support/TimeProfilerTest.cpp  | 106 +++++++++---------
 5 files changed, 95 insertions(+), 59 deletions(-)
 create mode 100644 clang/test/Interpreter/ftime-report.cpp

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 01f83498d8c8e..6d5c083b3af6c 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -220,6 +220,13 @@ class CompilerInstance : public ModuleLoader {
   /// @name High-Level Operations
   /// @{
 
+  // FIXME: Add a static InitializeProcess() method to consolidate process-level
+  // setup that is currently scattered across tool entry points (cc1_main,
+  // clang-repl, libclang, etc.). This would include things like AsmParsers and
+  // install_fatal_error_handler.
+  // These are process-global, so a single static method would  all clang-based
+  // tools to share them without duplication.
+
   /// ExecuteAction - Execute the provided action against the compiler's
   /// CompilerInvocation object.
   ///
@@ -812,6 +819,13 @@ class CompilerInstance : public ModuleLoader {
                    bool UseTemporary, bool CreateMissingDirectories = false);
 
 private:
+  /// Prepare the CompilerInstance for executing a frontend action.
+  ///
+  /// Called by ExecuteAction. Consolidates instance-level setup that was
+  /// previously duplicated across tool entry points (cc1_main,
+  /// clang-repl/Interpreter, etc.).
+  void PrepareForExecution();
+
   /// Create a new output file and add it to the list of tracked output files.
   ///
   /// If \p OutputPath is empty, then createOutputFile will derive an output
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 0b00ad7128c00..52d5ef0faae67 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -944,11 +944,31 @@ bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input,
 
 // High-Level Operations
 
+void CompilerInstance::PrepareForExecution() {
+  // Set up the frontend timer for -ftime-report. BackendConsumer uses
+  // getTimerGroup() and getFrontendTimer() when TimePasses is set. In the
+  // cc1 driver path this was done in cc1_main before calling
+  // ExecuteCompilerInvocation; we consolidate it here so that all tools
+  // (cc1, clang-repl, libclang, etc.) get consistent behavior.
+  if (getCodeGenOpts().TimePasses && !FrontendTimer) {
+    createFrontendTimer();
+    getFrontendTimer().startTimer();
+  }
+
+  // FIXME: Consider consolidating additional per-instance setup here:
+  // - llvm::timeTraceProfilerInitialize) when TimeTracePath is set.
+  // - Plugin loading (LoadRequestedPlugins) and -mllvm argument processing.
+}
+
 bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
   assert(hasDiagnostics() && "Diagnostics engine is not initialized!");
   assert(!getFrontendOpts().ShowHelp && "Client must handle '-help'!");
   assert(!getFrontendOpts().ShowVersion && "Client must handle '-version'!");
 
+  llvm::TimeTraceScope TimeScope("ExecuteCompiler");
+
+  PrepareForExecution();
+
   // Mark this point as the bottom of the stack if we don't have somewhere
   // better. We generally expect frontend actions to be invoked with (nearly)
   // DesiredStackSpace available.
diff --git a/clang/test/Interpreter/ftime-report.cpp b/clang/test/Interpreter/ftime-report.cpp
new file mode 100644
index 0000000000000..1c19a9bf0e79b
--- /dev/null
+++ b/clang/test/Interpreter/ftime-report.cpp
@@ -0,0 +1,5 @@
+// Tests that -ftime-report works with clang-repl without crashing.
+// RUN: clang-repl -Xcc -ftime-report "int x = 42;" 2>&1 | FileCheck %s
+// CHECK-NOT: Assertion
+// CHECK-NOT: PLEASE submit a bug report
+// CHECK: Clang time report
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 0a9ded1cf213a..35405044d8d37 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -293,14 +293,7 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
     return 1;
 
   // Execute the frontend actions.
-  {
-    llvm::TimeTraceScope TimeScope("ExecuteCompiler");
-    bool TimePasses = Clang->getCodeGenOpts().TimePasses;
-    if (TimePasses)
-      Clang->createFrontendTimer();
-    llvm::TimeRegion Timer(TimePasses ? &Clang->getFrontendTimer() : nullptr);
-    Success = ExecuteCompilerInvocation(Clang.get());
-  }
+  Success = ExecuteCompilerInvocation(Clang.get());
 
   // If any timers were active but haven't been destroyed yet, print their
   // results now.  This happens in -disable-free mode.
diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp
index 5eacae6d200eb..eb226a605cdb1 100644
--- a/clang/unittests/Support/TimeProfilerTest.cpp
+++ b/clang/unittests/Support/TimeProfilerTest.cpp
@@ -155,10 +155,10 @@ std::string buildTraceGraph(StringRef Json) {
 
       // Presumably due to timer rounding, PerformPendingInstantiations often
       // appear to be within the timer interval of the immediately previous
-      // event group. We always know these events occur at level 1, not level 2,
-      // in our tests, so pop an event in that case.
+      // event group. We always know these events occur at the top level
+      // (under ExecuteCompiler), so force-pop until we get there.
       if (InsideCurrentEvent && Event.Name == "PerformPendingInstantiations" &&
-          EventStack.size() == 2) {
+          EventStack.size() >= 2) {
         InsideCurrentEvent = false;
       }
 
@@ -219,30 +219,31 @@ constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
   ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
   std::string Json = teardownProfiler();
   ASSERT_EQ(R"(
-Frontend (test.cc)
-| ParseDeclarationOrFunctionDefinition (test.cc:2:1)
-| ParseDeclarationOrFunctionDefinition (test.cc:6:1)
-| | ParseFunctionDefinition (slow_func)
-| | | EvaluateAsRValue (<test.cc:8:21>)
-| | | EvaluateForOverflow (<test.cc:8:21, col:25>)
-| | | EvaluateForOverflow (<test.cc:8:30, col:32>)
-| | | EvaluateAsRValue (<test.cc:9:14>)
-| | | EvaluateForOverflow (<test.cc:9:9, col:14>)
-| | | isPotentialConstantExpr (slow_namespace::slow_func)
-| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
-| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
-| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
-| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
-| ParseDeclarationOrFunctionDefinition (test.cc:16:1)
-| | ParseFunctionDefinition (slow_test)
-| | | EvaluateAsInitializer (slow_value)
-| | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
-| | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
-| ParseDeclarationOrFunctionDefinition (test.cc:22:1)
-| | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
-| | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
-| ParseDeclarationOrFunctionDefinition (test.cc:25:1)
-| | EvaluateAsInitializer (slow_init_list)
+ExecuteCompiler
+| Frontend (test.cc)
+| | ParseDeclarationOrFunctionDefinition (test.cc:2:1)
+| | ParseDeclarationOrFunctionDefinition (test.cc:6:1)
+| | | ParseFunctionDefinition (slow_func)
+| | | | EvaluateAsRValue (<test.cc:8:21>)
+| | | | EvaluateForOverflow (<test.cc:8:21, col:25>)
+| | | | EvaluateForOverflow (<test.cc:8:30, col:32>)
+| | | | EvaluateAsRValue (<test.cc:9:14>)
+| | | | EvaluateForOverflow (<test.cc:9:9, col:14>)
+| | | | isPotentialConstantExpr (slow_namespace::slow_func)
+| | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
+| | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
+| | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
+| | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
+| | ParseDeclarationOrFunctionDefinition (test.cc:16:1)
+| | | ParseFunctionDefinition (slow_test)
+| | | | EvaluateAsInitializer (slow_value)
+| | | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
+| | | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
+| | ParseDeclarationOrFunctionDefinition (test.cc:22:1)
+| | | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
+| | | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
+| | ParseDeclarationOrFunctionDefinition (test.cc:25:1)
+| | | EvaluateAsInitializer (slow_init_list)
 | PerformPendingInstantiations
 )",
             buildTraceGraph(Json));
@@ -270,18 +271,19 @@ TEST(TimeProfilerTest, ClassTemplateInstantiations) {
   ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
   std::string Json = teardownProfiler();
   ASSERT_EQ(R"(
-Frontend (test.cc)
-| ParseClass (S)
-| CheckConstraintSatisfaction (<test.cc:9:21, col:29>)
-| InstantiateClass (S<double>, test.cc:9)
-| InstantiateFunction (S<double>::foo, test.cc:5)
-| ParseDeclarationOrFunctionDefinition (test.cc:11:5)
-| | ParseFunctionDefinition (user)
-| | | CheckConstraintSatisfaction (<test.cc:12:7, col:12>)
-| | | InstantiateClass (S<int>, test.cc:3)
-| | | CheckConstraintSatisfaction (<test.cc:13:7, col:14>)
-| | | InstantiateClass (S<float>, test.cc:3)
-| | | DeferInstantiation (S<float>::foo)
+ExecuteCompiler
+| Frontend (test.cc)
+| | ParseClass (S)
+| | CheckConstraintSatisfaction (<test.cc:9:21, col:29>)
+| | InstantiateClass (S<double>, test.cc:9)
+| | InstantiateFunction (S<double>::foo, test.cc:5)
+| | ParseDeclarationOrFunctionDefinition (test.cc:11:5)
+| | | ParseFunctionDefinition (user)
+| | | | CheckConstraintSatisfaction (<test.cc:12:7, col:12>)
+| | | | InstantiateClass (S<int>, test.cc:3)
+| | | | CheckConstraintSatisfaction (<test.cc:13:7, col:14>)
+| | | | InstantiateClass (S<float>, test.cc:3)
+| | | | DeferInstantiation (S<float>::foo)
 | PerformPendingInstantiations
 | | InstantiateFunction (S<float>::foo, test.cc:5)
 )",
@@ -321,14 +323,15 @@ TEST(TimeProfilerTest, TemplateInstantiations) {
                                 /*Headers=*/{{"a.h", A_H}, {"b.h", B_H}}));
   std::string Json = teardownProfiler();
   ASSERT_EQ(R"(
-Frontend (test.cc)
-| ParseFunctionDefinition (fooC)
-| ParseFunctionDefinition (fooB)
-| ParseFunctionDefinition (fooMTA)
-| ParseFunctionDefinition (fooA)
-| ParseDeclarationOrFunctionDefinition (test.cc:3:5)
-| | ParseFunctionDefinition (user)
-| | | DeferInstantiation (fooA<int>)
+ExecuteCompiler
+| Frontend (test.cc)
+| | ParseFunctionDefinition (fooC)
+| | ParseFunctionDefinition (fooB)
+| | ParseFunctionDefinition (fooMTA)
+| | ParseFunctionDefinition (fooA)
+| | ParseDeclarationOrFunctionDefinition (test.cc:3:5)
+| | | ParseFunctionDefinition (user)
+| | | | DeferInstantiation (fooA<int>)
 | PerformPendingInstantiations
 | | InstantiateFunction (fooA<int>, a.h:7)
 | | | InstantiateFunction (fooB<int>, b.h:8)
@@ -353,10 +356,11 @@ struct {
   ASSERT_TRUE(compileFromString(Code, "-std=c99", "test.c"));
   std::string Json = teardownProfiler();
   ASSERT_EQ(R"(
-Frontend (test.c)
-| ParseDeclarationOrFunctionDefinition (test.c:2:1)
-| | isIntegerConstantExpr (<test.c:3:18>)
-| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
+ExecuteCompiler
+| Frontend (test.c)
+| | ParseDeclarationOrFunctionDefinition (test.c:2:1)
+| | | isIntegerConstantExpr (<test.c:3:18>)
+| | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
 | PerformPendingInstantiations
 )",
             buildTraceGraph(Json));



More information about the cfe-commits mailing list