[ARM] Reduce loop unroll when low overhead branching is available (#120065)
[llvm-project.git] / clang / unittests / Support / TimeProfilerTest.cpp
blob995ebf625b7ab2e6cf40d173f0e3b191ece78877
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/ADT/StringMap.h"
14 #include "llvm/Support/JSON.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/TimeProfiler.h"
17 #include "llvm/Support/VirtualFileSystem.h"
18 #include <stack>
20 #include "gtest/gtest.h"
21 #include <tuple>
23 using namespace clang;
24 using namespace llvm;
26 namespace {
28 // Should be called before testing.
29 void setupProfiler() {
30 timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",
31 /*TimeTraceVerbose=*/true);
34 // Should be called after `compileFromString()`.
35 // Returns profiler's JSON dump.
36 std::string teardownProfiler() {
37 SmallVector<char, 1024> SmallVec;
38 raw_svector_ostream OS(SmallVec);
39 timeTraceProfilerWrite(OS);
40 timeTraceProfilerCleanup();
41 return OS.str().str();
44 // Returns true if code compiles successfully.
45 // We only parse AST here. This is enough for constexpr evaluation.
46 bool compileFromString(StringRef Code, StringRef Standard, StringRef File,
47 llvm::StringMap<std::string> Headers = {}) {
49 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS(
50 new llvm::vfs::InMemoryFileSystem());
51 FS->addFile(File, 0, MemoryBuffer::getMemBuffer(Code));
52 for (const auto &Header : Headers) {
53 FS->addFile(Header.getKey(), 0,
54 MemoryBuffer::getMemBuffer(Header.getValue()));
56 llvm::IntrusiveRefCntPtr<FileManager> Files(
57 new FileManager(FileSystemOptions(), FS));
58 CompilerInstance Compiler;
59 Compiler.createDiagnostics(Files->getVirtualFileSystem());
60 Compiler.setFileManager(Files.get());
62 auto Invocation = std::make_shared<CompilerInvocation>();
63 std::vector<const char *> Args = {Standard.data(), File.data()};
64 CompilerInvocation::CreateFromArgs(*Invocation, Args,
65 Compiler.getDiagnostics());
66 Compiler.setInvocation(std::move(Invocation));
68 class TestFrontendAction : public ASTFrontendAction {
69 private:
70 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
71 StringRef InFile) override {
72 return std::make_unique<ASTConsumer>();
74 } Action;
75 return Compiler.ExecuteAction(Action);
78 std::string GetMetadata(json::Object *Event) {
79 std::string M;
80 llvm::raw_string_ostream OS(M);
81 if (json::Object *Args = Event->getObject("args")) {
82 if (auto Detail = Args->getString("detail"))
83 OS << Detail;
84 // Use only filename to not include os-specific path separators.
85 if (auto File = Args->getString("file"))
86 OS << (M.empty() ? "" : ", ") << llvm::sys::path::filename(*File);
87 if (auto Line = Args->getInteger("line"))
88 OS << ":" << *Line;
90 return M;
93 // Returns pretty-printed trace graph.
94 std::string buildTraceGraph(StringRef Json) {
95 struct EventRecord {
96 int64_t TimestampBegin;
97 int64_t TimestampEnd;
98 std::string Name;
99 std::string Metadata;
101 std::vector<EventRecord> Events;
103 // Parse `EventRecord`s from JSON dump.
104 Expected<json::Value> Root = json::parse(Json);
105 if (!Root)
106 return "";
107 for (json::Value &TraceEventValue :
108 *Root->getAsObject()->getArray("traceEvents")) {
109 json::Object *TraceEventObj = TraceEventValue.getAsObject();
111 int64_t TimestampBegin = TraceEventObj->getInteger("ts").value_or(0);
112 int64_t TimestampEnd =
113 TimestampBegin + TraceEventObj->getInteger("dur").value_or(0);
114 std::string Name = TraceEventObj->getString("name").value_or("").str();
115 std::string Metadata = GetMetadata(TraceEventObj);
117 // Source events are asynchronous events and may not perfectly nest the
118 // synchronous events. Skip testing them.
119 if (Name == "Source")
120 continue;
122 // This is a "summary" event, like "Total PerformPendingInstantiations",
123 // skip it
124 if (TimestampBegin == 0)
125 continue;
127 Events.emplace_back(
128 EventRecord{TimestampBegin, TimestampEnd, Name, Metadata});
131 // There can be nested events that are very fast, for example:
132 // {"name":"EvaluateAsBooleanCondition",... ,"ts":2380,"dur":1}
133 // {"name":"EvaluateAsRValue",... ,"ts":2380,"dur":1}
134 // Therefore we should reverse the events list, so that events that have
135 // started earlier are first in the list.
136 // Then do a stable sort, we need it for the trace graph.
137 std::reverse(Events.begin(), Events.end());
138 std::stable_sort(
139 Events.begin(), Events.end(), [](const auto &lhs, const auto &rhs) {
140 return std::make_pair(lhs.TimestampBegin, -lhs.TimestampEnd) <
141 std::make_pair(rhs.TimestampBegin, -rhs.TimestampEnd);
144 std::stringstream Stream;
145 // Write a newline for better testing with multiline string literal.
146 Stream << "\n";
148 // Keep the current event stack.
149 std::stack<const EventRecord *> EventStack;
150 for (const auto &Event : Events) {
151 // Pop every event in the stack until meeting the parent event.
152 while (!EventStack.empty()) {
153 bool InsideCurrentEvent =
154 Event.TimestampBegin >= EventStack.top()->TimestampBegin &&
155 Event.TimestampEnd <= EventStack.top()->TimestampEnd;
156 if (!InsideCurrentEvent)
157 EventStack.pop();
158 else
159 break;
161 EventStack.push(&Event);
163 // Write indentaion, name, detail, newline.
164 for (size_t i = 1; i < EventStack.size(); ++i) {
165 Stream << "| ";
167 Stream.write(Event.Name.data(), Event.Name.size());
168 if (!Event.Metadata.empty()) {
169 Stream << " (";
170 Stream.write(Event.Metadata.data(), Event.Metadata.size());
171 Stream << ")";
173 Stream << "\n";
175 return Stream.str();
178 } // namespace
180 TEST(TimeProfilerTest, ConstantEvaluationCxx20) {
181 std::string Code = R"(
182 void print(double value);
184 namespace slow_namespace {
186 consteval double slow_func() {
187 double d = 0.0;
188 for (int i = 0; i < 100; ++i) { // 8th line
189 d += i; // 9th line
191 return d;
194 } // namespace slow_namespace
196 void slow_test() {
197 constexpr auto slow_value = slow_namespace::slow_func(); // 17th line
198 print(slow_namespace::slow_func()); // 18th line
199 print(slow_value);
202 int slow_arr[12 + 34 * 56 + // 22nd line
203 static_cast<int>(slow_namespace::slow_func())]; // 23rd line
205 constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
208 setupProfiler();
209 ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
210 std::string Json = teardownProfiler();
211 ASSERT_EQ(R"(
212 Frontend (test.cc)
213 | ParseDeclarationOrFunctionDefinition (test.cc:2:1)
214 | ParseDeclarationOrFunctionDefinition (test.cc:6:1)
215 | | ParseFunctionDefinition (slow_func)
216 | | | EvaluateAsRValue (<test.cc:8:21>)
217 | | | EvaluateForOverflow (<test.cc:8:21, col:25>)
218 | | | EvaluateForOverflow (<test.cc:8:30, col:32>)
219 | | | EvaluateAsRValue (<test.cc:9:14>)
220 | | | EvaluateForOverflow (<test.cc:9:9, col:14>)
221 | | | isPotentialConstantExpr (slow_namespace::slow_func)
222 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
223 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
224 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
225 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
226 | ParseDeclarationOrFunctionDefinition (test.cc:16:1)
227 | | ParseFunctionDefinition (slow_test)
228 | | | EvaluateAsInitializer (slow_value)
229 | | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
230 | | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
231 | ParseDeclarationOrFunctionDefinition (test.cc:22:1)
232 | | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
233 | | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
234 | ParseDeclarationOrFunctionDefinition (test.cc:25:1)
235 | | EvaluateAsInitializer (slow_init_list)
236 | PerformPendingInstantiations
238 buildTraceGraph(Json));
241 TEST(TimeProfilerTest, ClassTemplateInstantiations) {
242 std::string Code = R"(
243 template<class T>
244 struct S
246 void foo() {}
247 void bar();
250 template struct S<double>; // explicit instantiation of S<double>
252 void user() {
253 S<int> a; // implicit instantiation of S<int>
254 S<float>* b;
255 b->foo(); // implicit instatiation of S<float> and S<float>::foo()
259 setupProfiler();
260 ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));
261 std::string Json = teardownProfiler();
262 ASSERT_EQ(R"(
263 Frontend (test.cc)
264 | ParseClass (S)
265 | InstantiateClass (S<double>, test.cc:9)
266 | InstantiateFunction (S<double>::foo, test.cc:5)
267 | ParseDeclarationOrFunctionDefinition (test.cc:11:5)
268 | | ParseFunctionDefinition (user)
269 | | | InstantiateClass (S<int>, test.cc:3)
270 | | | InstantiateClass (S<float>, test.cc:3)
271 | | | DeferInstantiation (S<float>::foo)
272 | PerformPendingInstantiations
273 | | InstantiateFunction (S<float>::foo, test.cc:5)
275 buildTraceGraph(Json));
278 TEST(TimeProfilerTest, TemplateInstantiations) {
279 std::string B_H = R"(
280 template <typename T>
281 T fooC(T t) {
282 return T();
285 template <typename T>
286 constexpr T fooB(T t) {
287 return fooC(t);
290 #define MacroTemp(x) template <typename T> void foo##x(T) { T(); }
293 std::string A_H = R"(
294 #include "b.h"
296 MacroTemp(MTA)
298 template <typename T>
299 void fooA(T t) { fooB(t); fooMTA(t); }
301 std::string Code = R"(
302 #include "a.h"
303 void user() { fooA(0); }
306 setupProfiler();
307 ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc",
308 /*Headers=*/{{"a.h", A_H}, {"b.h", B_H}}));
309 std::string Json = teardownProfiler();
310 ASSERT_EQ(R"(
311 Frontend (test.cc)
312 | ParseFunctionDefinition (fooC)
313 | ParseFunctionDefinition (fooB)
314 | ParseFunctionDefinition (fooMTA)
315 | ParseFunctionDefinition (fooA)
316 | ParseDeclarationOrFunctionDefinition (test.cc:3:5)
317 | | ParseFunctionDefinition (user)
318 | | | DeferInstantiation (fooA<int>)
319 | PerformPendingInstantiations
320 | | InstantiateFunction (fooA<int>, a.h:7)
321 | | | InstantiateFunction (fooB<int>, b.h:8)
322 | | | | DeferInstantiation (fooC<int>)
323 | | | DeferInstantiation (fooMTA<int>)
324 | | | InstantiateFunction (fooC<int>, b.h:3)
325 | | | InstantiateFunction (fooMTA<int>, a.h:4)
327 buildTraceGraph(Json));
330 TEST(TimeProfilerTest, ConstantEvaluationC99) {
331 std::string Code = R"(
332 struct {
333 short quantval[4]; // 3rd line
334 } value;
337 setupProfiler();
338 ASSERT_TRUE(compileFromString(Code, "-std=c99", "test.c"));
339 std::string Json = teardownProfiler();
340 ASSERT_EQ(R"(
341 Frontend (test.c)
342 | ParseDeclarationOrFunctionDefinition (test.c:2:1)
343 | | isIntegerConstantExpr (<test.c:3:18>)
344 | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
345 | PerformPendingInstantiations
347 buildTraceGraph(Json));