Izaron updated this revision to Diff 469098.
Izaron added a comment.

Fix CMakeLists.txt


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D136022/new/

https://reviews.llvm.org/D136022

Files:
  clang/lib/AST/ExprConstant.cpp
  clang/unittests/CMakeLists.txt
  clang/unittests/Support/CMakeLists.txt
  clang/unittests/Support/TimeProfilerTest.cpp

Index: clang/unittests/Support/TimeProfilerTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Support/TimeProfilerTest.cpp
@@ -0,0 +1,198 @@
+//===- unittests/Support/TimeProfilerTest.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/PreprocessorOptions.h"
+
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/TimeProfiler.h"
+
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+// Should be called before testing.
+void setupProfiler() {
+  timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test");
+}
+
+// Should be called after `compileFromString()`.
+// Returns profiler's JSON dump.
+std::string teardownProfiler() {
+  SmallVector<char, 1024> SmallVec;
+  raw_svector_ostream OS(SmallVec);
+  timeTraceProfilerWrite(OS);
+  timeTraceProfilerCleanup();
+  return OS.str().str();
+}
+
+// Returns true if code compiles successfully.
+// We only parse AST here. This is enough for constexpr evaluation.
+bool compileFromString(StringRef Code) {
+  CompilerInstance Compiler;
+  Compiler.createDiagnostics();
+
+  auto Invocation = std::make_shared<CompilerInvocation>();
+  Invocation->getPreprocessorOpts().addRemappedFile(
+      "test.cc", MemoryBuffer::getMemBuffer(Code).release());
+  const char *Args[] = {"-std=c++20", "test.cc"};
+  CompilerInvocation::CreateFromArgs(*Invocation, Args,
+                                     Compiler.getDiagnostics());
+  Compiler.setInvocation(std::move(Invocation));
+
+  class TestFrontendAction : public ASTFrontendAction {
+  private:
+    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                   StringRef InFile) override {
+      return std::make_unique<ASTConsumer>();
+    }
+  } Action;
+  return Compiler.ExecuteAction(Action);
+}
+
+// Returns pretty-printed trace graph.
+std::string buildTraceGraph(StringRef Json) {
+  struct EventRecord {
+    int64_t TimestampBegin;
+    int64_t TimestampEnd;
+    StringRef Name;
+    StringRef Detail;
+  };
+  std::vector<EventRecord> Events;
+
+  // Parse `EventRecord`s from JSON dump.
+  Expected<json::Value> Root = json::parse(Json);
+  if (!Root)
+    return "";
+  for (json::Value &TraceEventValue :
+       *Root->getAsObject()->getArray("traceEvents")) {
+    json::Object *TraceEventObj = TraceEventValue.getAsObject();
+
+    int64_t TimestampBegin = *TraceEventObj->getInteger("ts");
+    int64_t TimestampEnd = TimestampBegin + *TraceEventObj->getInteger("dur");
+    StringRef Name = *TraceEventObj->getString("name");
+    StringRef Detail = "";
+    if (json::Object *Args = TraceEventObj->getObject("args"))
+      Detail = Args->getString("detail").value_or("");
+
+    // This is a "summary" event, like "Total PerformPendingInstantiations",
+    // skip it
+    if (TimestampBegin == 0)
+      continue;
+
+    Events.emplace_back(
+        EventRecord{TimestampBegin, TimestampEnd, Name, Detail});
+  }
+
+  // There can be nested events that are very fast, for example:
+  // {"name":"EvaluateAsBooleanCondition",... ,"ts":2380,"dur":1}
+  // {"name":"EvaluateAsRValue",... ,"ts":2380,"dur":1}
+  // Therefore we should reverse the events list, so that events that have
+  // started earlier are first in the list.
+  // Then do a stable sort, we need it for the trace graph.
+  std::reverse(Events.begin(), Events.end());
+  std::stable_sort(
+      Events.begin(), Events.end(), [](const auto &lhs, const auto &rhs) {
+        return std::make_pair(lhs.TimestampBegin, lhs.TimestampEnd) <
+               std::make_pair(rhs.TimestampBegin, rhs.TimestampEnd);
+      });
+
+  std::stringstream Stream;
+  // Write a newline for better testing with multiline string literal.
+  Stream << "\n";
+
+  // Keep the current event stack.
+  std::stack<const EventRecord *> EventStack;
+  for (const auto &Event : Events) {
+    // Pop every event in the stack until meeting the parent event.
+    while (!EventStack.empty()) {
+      bool InsideCurrentEvent =
+          Event.TimestampBegin >= EventStack.top()->TimestampBegin &&
+          Event.TimestampEnd <= EventStack.top()->TimestampEnd;
+      if (!InsideCurrentEvent)
+        EventStack.pop();
+      else
+        break;
+    }
+    EventStack.push(&Event);
+
+    // Write indentaion, name, detail, newline.
+    for (size_t i = 1; i < EventStack.size(); ++i) {
+      Stream << "| ";
+    }
+    Stream.write(Event.Name.data(), Event.Name.size());
+    if (!Event.Detail.empty()) {
+      Stream << " (";
+      Stream.write(Event.Detail.data(), Event.Detail.size());
+      Stream << ")";
+    }
+    Stream << "\n";
+  }
+  return Stream.str();
+}
+
+} // namespace
+
+TEST(TimeProfilerTest, ConstantEvaluation) {
+  constexpr StringRef Code = R"(
+void print(double value);
+
+namespace slow_namespace {
+
+consteval double slow_func() {
+    double d = 0.0;
+    for (int i = 0; i < 100; ++i) { // 8th line
+        d += i;                     // 9th line
+    }
+    return d;
+}
+
+} // namespace slow_namespace
+
+void slow_test() {
+    constexpr auto slow_value = slow_namespace::slow_func(); // 17th line
+    print(slow_namespace::slow_func());                      // 18th line
+    print(slow_value);
+}
+
+int slow_arr[12 + 34 * 56 +                                  // 22nd line
+             static_cast<int>(slow_namespace::slow_func())]; // 23rd line
+
+constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
+    )";
+
+  setupProfiler();
+  ASSERT_TRUE(compileFromString(Code));
+  std::string Json = teardownProfiler();
+  std::string TraceGraph = buildTraceGraph(Json);
+  ASSERT_TRUE(TraceGraph == R"(
+Frontend
+| EvaluateAsRValue (<test.cc:8:21>)
+| EvaluateForOverflow (<test.cc:8:21, col:25>)
+| 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>)
+| EvaluateAsInitializer (slow_value)
+| EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
+| EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
+| EvaluateAsRValue (<test.cc:22:14, line:23:58>)
+| EvaluateAsInitializer (slow_init_list)
+| PerformPendingInstantiations
+)");
+
+  // NOTE: If this test is failing, run this test with
+  // `llvm::cerr() << TraceGraph;` and change the assert above.
+}
Index: clang/unittests/Support/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang/unittests/Support/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_unittest(ClangSupportTests
+  TimeProfilerTest.cpp
+  )
+
+clang_target_link_libraries(ClangSupportTests
+  PRIVATE
+  clangFrontend
+  )
Index: clang/unittests/CMakeLists.txt
===================================================================
--- clang/unittests/CMakeLists.txt
+++ clang/unittests/CMakeLists.txt
@@ -47,3 +47,4 @@
 add_subdirectory(Rename)
 add_subdirectory(Index)
 add_subdirectory(Serialization)
+add_subdirectory(Support)
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -56,6 +56,7 @@
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstring>
 #include <functional>
@@ -660,6 +661,19 @@
     CallStackFrame &Frame;
     const LValue *OldThis;
   };
+
+  // A shorthand time trace scope struct, prints source range, for example
+  // {"name":"EvaluateAsRValue","args":{"detail":"<test.cc:8:21, col:25>"}}}
+  class ExprTimeTraceScope {
+  public:
+    ExprTimeTraceScope(const Expr *E, const ASTContext &Ctx, StringRef Name)
+        : TimeScope(Name, [E, &Ctx] {
+            return E->getSourceRange().printToString(Ctx.getSourceManager());
+          }) {}
+
+  private:
+    llvm::TimeTraceScope TimeScope;
+  };
 }
 
 static bool HandleDestruction(EvalInfo &Info, const Expr *E,
@@ -15093,6 +15107,7 @@
                             bool InConstantContext) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsRValue");
   EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsRValue(this, Result, Ctx, Info);
@@ -15102,6 +15117,7 @@
                                       bool InConstantContext) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsBooleanCondition");
   EvalResult Scratch;
   return EvaluateAsRValue(Scratch, Ctx, InConstantContext) &&
          HandleConversionToBool(Scratch.Val, Result);
@@ -15112,6 +15128,7 @@
                          bool InConstantContext) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsInt");
   EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info);
@@ -15122,6 +15139,7 @@
                                 bool InConstantContext) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFixedPoint");
   EvalInfo Info(Ctx, Result, EvalInfo::EM_IgnoreSideEffects);
   Info.InConstantContext = InConstantContext;
   return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info);
@@ -15136,6 +15154,7 @@
   if (!getType()->isRealFloatingType())
     return false;
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFloat");
   EvalResult ExprResult;
   if (!EvaluateAsRValue(ExprResult, Ctx, InConstantContext) ||
       !ExprResult.Val.isFloat() ||
@@ -15151,6 +15170,7 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue");
   EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
   Info.InConstantContext = InConstantContext;
   LValue LV;
@@ -15195,6 +15215,7 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr");
   EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression;
   EvalInfo Info(Ctx, Result, EM);
   Info.InConstantContext = true;
@@ -15247,6 +15268,13 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] {
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    VD->printQualifiedName(OS);
+    return Name;
+  });
+
   // FIXME: Evaluating initializers for large array and record types can cause
   // performance problems. Only do so in C++11 for now.
   if (isPRValue() && (getType()->isArrayType() || getType()->isRecordType()) &&
@@ -15335,6 +15363,7 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstInt");
   EvalResult EVResult;
   EVResult.Diag = Diag;
   EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects);
@@ -15353,6 +15382,7 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstIntCheckOverflow");
   EvalResult EVResult;
   EVResult.Diag = Diag;
   EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects);
@@ -15371,6 +15401,7 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateForOverflow");
   bool IsConst;
   EvalResult EVResult;
   if (!FastEvaluateAsRValue(this, EVResult, Ctx, IsConst)) {
@@ -15862,6 +15893,10 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  llvm::TimeTraceScope TimeScope("isIntegerConstantExpr", [&] {
+    return Loc->printToString(Ctx.getSourceManager());
+  });
+
   if (Ctx.getLangOpts().CPlusPlus11)
     return EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, nullptr, Loc);
 
@@ -15954,6 +15989,14 @@
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
+  llvm::TimeTraceScope TimeScope("EvaluateWithSubstitution", [&] {
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    Callee->getNameForDiagnostic(OS, Ctx.getPrintingPolicy(),
+                                 /*Qualified=*/true);
+    return Name;
+  });
+
   Expr::EvalStatus Status;
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpressionUnevaluated);
   Info.InConstantContext = true;
@@ -16018,6 +16061,14 @@
   if (FD->isDependentContext())
     return true;
 
+  llvm::TimeTraceScope TimeScope("isPotentialConstantExpr", [&] {
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    FD->getNameForDiagnostic(OS, FD->getASTContext().getPrintingPolicy(),
+                             /*Qualified=*/true);
+    return Name;
+  });
+
   Expr::EvalStatus Status;
   Status.Diag = &Diags;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to