Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Support / TimeProfilerTest.cpp
blob97fdbb7232b1351f94eb0c1aa956c45ca19bf8cb
1 //===- unittests/Support/TimeProfilerTest.cpp -----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/CompilerInstance.h"
10 #include "clang/Frontend/FrontendActions.h"
11 #include "clang/Lex/PreprocessorOptions.h"
13 #include "llvm/Support/JSON.h"
14 #include "llvm/Support/TimeProfiler.h"
16 #include "gtest/gtest.h"
18 using namespace clang;
19 using namespace llvm;
21 namespace {
23 // Should be called before testing.
24 void setupProfiler() {
25 timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test");
28 // Should be called after `compileFromString()`.
29 // Returns profiler's JSON dump.
30 std::string teardownProfiler() {
31 SmallVector<char, 1024> SmallVec;
32 raw_svector_ostream OS(SmallVec);
33 timeTraceProfilerWrite(OS);
34 timeTraceProfilerCleanup();
35 return OS.str().str();
38 // Returns true if code compiles successfully.
39 // We only parse AST here. This is enough for constexpr evaluation.
40 bool compileFromString(StringRef Code, StringRef Standard, StringRef FileName) {
41 CompilerInstance Compiler;
42 Compiler.createDiagnostics();
44 auto Invocation = std::make_shared<CompilerInvocation>();
45 Invocation->getPreprocessorOpts().addRemappedFile(
46 FileName, MemoryBuffer::getMemBuffer(Code).release());
47 const char *Args[] = {Standard.data(), FileName.data()};
48 CompilerInvocation::CreateFromArgs(*Invocation, Args,
49 Compiler.getDiagnostics());
50 Compiler.setInvocation(std::move(Invocation));
52 class TestFrontendAction : public ASTFrontendAction {
53 private:
54 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
55 StringRef InFile) override {
56 return std::make_unique<ASTConsumer>();
58 } Action;
59 return Compiler.ExecuteAction(Action);
62 // Returns pretty-printed trace graph.
63 std::string buildTraceGraph(StringRef Json) {
64 struct EventRecord {
65 int64_t TimestampBegin;
66 int64_t TimestampEnd;
67 StringRef Name;
68 StringRef Detail;
70 std::vector<EventRecord> Events;
72 // Parse `EventRecord`s from JSON dump.
73 Expected<json::Value> Root = json::parse(Json);
74 if (!Root)
75 return "";
76 for (json::Value &TraceEventValue :
77 *Root->getAsObject()->getArray("traceEvents")) {
78 json::Object *TraceEventObj = TraceEventValue.getAsObject();
80 int64_t TimestampBegin = TraceEventObj->getInteger("ts").value_or(0);
81 int64_t TimestampEnd =
82 TimestampBegin + TraceEventObj->getInteger("dur").value_or(0);
83 StringRef Name = TraceEventObj->getString("name").value_or("");
84 StringRef Detail = "";
85 if (json::Object *Args = TraceEventObj->getObject("args"))
86 Detail = Args->getString("detail").value_or("");
88 // This is a "summary" event, like "Total PerformPendingInstantiations",
89 // skip it
90 if (TimestampBegin == 0)
91 continue;
93 Events.emplace_back(
94 EventRecord{TimestampBegin, TimestampEnd, Name, Detail});
97 // There can be nested events that are very fast, for example:
98 // {"name":"EvaluateAsBooleanCondition",... ,"ts":2380,"dur":1}
99 // {"name":"EvaluateAsRValue",... ,"ts":2380,"dur":1}
100 // Therefore we should reverse the events list, so that events that have
101 // started earlier are first in the list.
102 // Then do a stable sort, we need it for the trace graph.
103 std::reverse(Events.begin(), Events.end());
104 std::stable_sort(
105 Events.begin(), Events.end(), [](const auto &lhs, const auto &rhs) {
106 return std::make_pair(lhs.TimestampBegin, -lhs.TimestampEnd) <
107 std::make_pair(rhs.TimestampBegin, -rhs.TimestampEnd);
110 std::stringstream Stream;
111 // Write a newline for better testing with multiline string literal.
112 Stream << "\n";
114 // Keep the current event stack.
115 std::stack<const EventRecord *> EventStack;
116 for (const auto &Event : Events) {
117 // Pop every event in the stack until meeting the parent event.
118 while (!EventStack.empty()) {
119 bool InsideCurrentEvent =
120 Event.TimestampBegin >= EventStack.top()->TimestampBegin &&
121 Event.TimestampEnd <= EventStack.top()->TimestampEnd;
122 if (!InsideCurrentEvent)
123 EventStack.pop();
124 else
125 break;
127 EventStack.push(&Event);
129 // Write indentaion, name, detail, newline.
130 for (size_t i = 1; i < EventStack.size(); ++i) {
131 Stream << "| ";
133 Stream.write(Event.Name.data(), Event.Name.size());
134 if (!Event.Detail.empty()) {
135 Stream << " (";
136 Stream.write(Event.Detail.data(), Event.Detail.size());
137 Stream << ")";
139 Stream << "\n";
141 return Stream.str();
144 } // namespace
146 TEST(TimeProfilerTest, ConstantEvaluationCxx20) {
147 constexpr StringRef Code = R"(
148 void print(double value);
150 namespace slow_namespace {
152 consteval double slow_func() {
153 double d = 0.0;
154 for (int i = 0; i < 100; ++i) { // 8th line
155 d += i; // 9th line
157 return d;
160 } // namespace slow_namespace
162 void slow_test() {
163 constexpr auto slow_value = slow_namespace::slow_func(); // 17th line
164 print(slow_namespace::slow_func()); // 18th line
165 print(slow_value);
168 int slow_arr[12 + 34 * 56 + // 22nd line
169 static_cast<int>(slow_namespace::slow_func())]; // 23rd line
171 constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
174 setupProfiler();
175 ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
176 std::string Json = teardownProfiler();
177 std::string TraceGraph = buildTraceGraph(Json);
178 ASSERT_TRUE(TraceGraph == R"(
179 Frontend
180 | ParseDeclarationOrFunctionDefinition (test.cc:2:1)
181 | ParseDeclarationOrFunctionDefinition (test.cc:6:1)
182 | | ParseFunctionDefinition (slow_func)
183 | | | EvaluateAsRValue (<test.cc:8:21>)
184 | | | EvaluateForOverflow (<test.cc:8:21, col:25>)
185 | | | EvaluateForOverflow (<test.cc:8:30, col:32>)
186 | | | EvaluateAsRValue (<test.cc:9:14>)
187 | | | EvaluateForOverflow (<test.cc:9:9, col:14>)
188 | | | isPotentialConstantExpr (slow_namespace::slow_func)
189 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
190 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
191 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
192 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
193 | ParseDeclarationOrFunctionDefinition (test.cc:16:1)
194 | | ParseFunctionDefinition (slow_test)
195 | | | EvaluateAsInitializer (slow_value)
196 | | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
197 | | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
198 | ParseDeclarationOrFunctionDefinition (test.cc:22:1)
199 | | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
200 | | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
201 | ParseDeclarationOrFunctionDefinition (test.cc:25:1)
202 | | EvaluateAsInitializer (slow_init_list)
203 | PerformPendingInstantiations
204 )");
206 // NOTE: If this test is failing, run this test with
207 // `llvm::errs() << TraceGraph;` and change the assert above.
210 TEST(TimeProfilerTest, ConstantEvaluationC99) {
211 constexpr StringRef Code = R"(
212 struct {
213 short quantval[4]; // 3rd line
214 } value;
217 setupProfiler();
218 ASSERT_TRUE(compileFromString(Code, "-std=c99", "test.c"));
219 std::string Json = teardownProfiler();
220 std::string TraceGraph = buildTraceGraph(Json);
221 ASSERT_TRUE(TraceGraph == R"(
222 Frontend
223 | ParseDeclarationOrFunctionDefinition (test.c:2:1)
224 | | isIntegerConstantExpr (<test.c:3:18>)
225 | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
226 | PerformPendingInstantiations
227 )");
229 // NOTE: If this test is failing, run this test with
230 // `llvm::errs() << TraceGraph;` and change the assert above.