mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-01 23:06:11 +00:00

This reverts commit 491b2810fb7fe5f080fa9c4f5945ed0a6909dc92. This change broke valid code and generated incorrect diagnostics, see https://reviews.llvm.org/D155064
223 lines
7.1 KiB
C++
223 lines
7.1 KiB
C++
//===- 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, StringRef Standard, StringRef FileName) {
|
|
CompilerInstance Compiler;
|
|
Compiler.createDiagnostics();
|
|
|
|
auto Invocation = std::make_shared<CompilerInvocation>();
|
|
Invocation->getPreprocessorOpts().addRemappedFile(
|
|
FileName, MemoryBuffer::getMemBuffer(Code).release());
|
|
const char *Args[] = {Standard.data(), FileName.data()};
|
|
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").value_or(0);
|
|
int64_t TimestampEnd =
|
|
TimestampBegin + TraceEventObj->getInteger("dur").value_or(0);
|
|
StringRef Name = TraceEventObj->getString("name").value_or("");
|
|
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, ConstantEvaluationCxx20) {
|
|
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=c++20", "test.cc"));
|
|
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>)
|
|
| 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>)
|
|
| 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::errs() << TraceGraph;` and change the assert above.
|
|
}
|
|
|
|
TEST(TimeProfilerTest, ConstantEvaluationC99) {
|
|
constexpr StringRef Code = R"(
|
|
struct {
|
|
short quantval[4]; // 3rd line
|
|
} value;
|
|
)";
|
|
|
|
setupProfiler();
|
|
ASSERT_TRUE(compileFromString(Code, "-std=c99", "test.c"));
|
|
std::string Json = teardownProfiler();
|
|
std::string TraceGraph = buildTraceGraph(Json);
|
|
ASSERT_TRUE(TraceGraph == R"(
|
|
Frontend
|
|
| isIntegerConstantExpr (<test.c:3:18>)
|
|
| EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
|
|
| PerformPendingInstantiations
|
|
)");
|
|
|
|
// NOTE: If this test is failing, run this test with
|
|
// `llvm::errs() << TraceGraph;` and change the assert above.
|
|
}
|