[llvm] [tools] LLVM Advisor - optimization analysis and performance guidance tool (PR #147451)
Miguel Cárdenas via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 12 21:21:12 PDT 2025
https://github.com/miguelcsx updated https://github.com/llvm/llvm-project/pull/147451
>From 429ae143a09775ea9e5ab8854b9b695edd7cf63f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20C=C3=A1rdenas?= <miguelecsx at gmail.com>
Date: Tue, 8 Jul 2025 04:41:10 +0200
Subject: [PATCH 1/4] [llvm-advisor] add initial project structure and
configuration
The AdvisorConfig class provides JSON based configuration loading
with file classification patterns and output directory management.
---
llvm/tools/llvm-advisor/CMakeLists.txt | 15 +++++
llvm/tools/llvm-advisor/config/config.json | 7 ++
llvm/tools/llvm-advisor/src/CMakeLists.txt | 35 ++++++++++
.../llvm-advisor/src/Config/AdvisorConfig.cpp | 64 +++++++++++++++++++
.../llvm-advisor/src/Config/AdvisorConfig.h | 41 ++++++++++++
5 files changed, 162 insertions(+)
create mode 100644 llvm/tools/llvm-advisor/CMakeLists.txt
create mode 100644 llvm/tools/llvm-advisor/config/config.json
create mode 100644 llvm/tools/llvm-advisor/src/CMakeLists.txt
create mode 100644 llvm/tools/llvm-advisor/src/Config/AdvisorConfig.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Config/AdvisorConfig.h
diff --git a/llvm/tools/llvm-advisor/CMakeLists.txt b/llvm/tools/llvm-advisor/CMakeLists.txt
new file mode 100644
index 0000000000000..d2389bdd1e0fa
--- /dev/null
+++ b/llvm/tools/llvm-advisor/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.18)
+
+set(LLVM_TOOL_LLVM_ADVISOR_BUILD_DEFAULT ON)
+set(LLVM_REQUIRE_EXE_NAMES llvm-advisor)
+
+add_subdirectory(src)
+
+# Set the executable name
+set_target_properties(llvm-advisor PROPERTIES
+ OUTPUT_NAME llvm-advisor)
+
+# Install the binary
+install(TARGETS llvm-advisor
+ RUNTIME DESTINATION bin
+ COMPONENT llvm-advisor)
diff --git a/llvm/tools/llvm-advisor/config/config.json b/llvm/tools/llvm-advisor/config/config.json
new file mode 100644
index 0000000000000..9e94a41ff46c4
--- /dev/null
+++ b/llvm/tools/llvm-advisor/config/config.json
@@ -0,0 +1,7 @@
+{
+ "outputDir": ".llvm-advisor",
+ "verbose": false,
+ "keepTemps": false,
+ "runProfiler": true,
+ "timeout": 60
+}
diff --git a/llvm/tools/llvm-advisor/src/CMakeLists.txt b/llvm/tools/llvm-advisor/src/CMakeLists.txt
new file mode 100644
index 0000000000000..81088f8231625
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Gather all .cpp sources in this directory tree
+file(GLOB_RECURSE LLVM_ADVISOR_SOURCES CONFIGURE_DEPENDS
+ ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
+)
+
+# Define the executable target
+add_llvm_tool(llvm-advisor
+ ${LLVM_ADVISOR_SOURCES}
+)
+
+# Link required LLVM libraries
+target_link_libraries(llvm-advisor PRIVATE
+ LLVMSupport
+ LLVMCore
+ LLVMIRReader
+ LLVMBitWriter
+ LLVMRemarks
+ LLVMProfileData
+)
+
+# Set include directories
+target_include_directories(llvm-advisor PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+# Install the Python view module alongside the binary
+install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../view/
+ DESTINATION ${CMAKE_INSTALL_BINDIR}/view
+ FILES_MATCHING
+ PATTERN "*.py"
+ PATTERN "*.html"
+ PATTERN "*.css"
+ PATTERN "*.js"
+ PATTERN "*.md"
+)
diff --git a/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.cpp b/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.cpp
new file mode 100644
index 0000000000000..69f1e3d52702e
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.cpp
@@ -0,0 +1,64 @@
+#include "AdvisorConfig.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+
+namespace llvm {
+namespace advisor {
+
+AdvisorConfig::AdvisorConfig() {
+ // Use relative path as default, will be resolved by CompilationManager
+ OutputDir_ = ".llvm-advisor";
+}
+
+Expected<bool> AdvisorConfig::loadFromFile(const std::string &path) {
+ auto BufferOrError = MemoryBuffer::getFile(path);
+ if (!BufferOrError) {
+ return createStringError(BufferOrError.getError(),
+ "Cannot read config file");
+ }
+
+ auto Buffer = std::move(*BufferOrError);
+ Expected<json::Value> JsonOrError = json::parse(Buffer->getBuffer());
+ if (!JsonOrError) {
+ return JsonOrError.takeError();
+ }
+
+ auto &Json = *JsonOrError;
+ auto *Obj = Json.getAsObject();
+ if (!Obj) {
+ return createStringError(std::make_error_code(std::errc::invalid_argument),
+ "Config file must contain JSON object");
+ }
+
+ if (auto outputDirOpt = Obj->getString("outputDir"); outputDirOpt) {
+ OutputDir_ = outputDirOpt->str();
+ }
+
+ if (auto verboseOpt = Obj->getBoolean("verbose"); verboseOpt) {
+ Verbose_ = *verboseOpt;
+ }
+
+ if (auto keepTempsOpt = Obj->getBoolean("keepTemps"); keepTempsOpt) {
+ KeepTemps_ = *keepTempsOpt;
+ }
+
+ if (auto runProfileOpt = Obj->getBoolean("runProfiler"); runProfileOpt) {
+ RunProfiler_ = *runProfileOpt;
+ }
+
+ if (auto timeoutOpt = Obj->getInteger("timeout"); timeoutOpt) {
+ TimeoutSeconds_ = static_cast<int>(*timeoutOpt);
+ }
+
+ return true;
+}
+
+std::string AdvisorConfig::getToolPath(const std::string &tool) const {
+ // For now, just return the tool name and rely on PATH
+ return tool;
+}
+
+} // namespace advisor
+} // namespace llvm
diff --git a/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.h b/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.h
new file mode 100644
index 0000000000000..b7f553fddbb23
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Config/AdvisorConfig.h
@@ -0,0 +1,41 @@
+#ifndef LLVM_ADVISOR_CONFIG_H
+#define LLVM_ADVISOR_CONFIG_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace llvm {
+namespace advisor {
+
+class AdvisorConfig {
+public:
+ AdvisorConfig();
+
+ Expected<bool> loadFromFile(const std::string &path);
+
+ void setOutputDir(const std::string &dir) { OutputDir_ = dir; }
+ void setVerbose(bool verbose) { Verbose_ = verbose; }
+ void setKeepTemps(bool keep) { KeepTemps_ = keep; }
+ void setRunProfiler(bool run) { RunProfiler_ = run; }
+ void setTimeout(int seconds) { TimeoutSeconds_ = seconds; }
+
+ const std::string &getOutputDir() const { return OutputDir_; }
+ bool getVerbose() const { return Verbose_; }
+ bool getKeepTemps() const { return KeepTemps_; }
+ bool getRunProfiler() const { return RunProfiler_; }
+ int getTimeout() const { return TimeoutSeconds_; }
+
+ std::string getToolPath(const std::string &tool) const;
+
+private:
+ std::string OutputDir_;
+ bool Verbose_ = false;
+ bool KeepTemps_ = false;
+ bool RunProfiler_ = true;
+ int TimeoutSeconds_ = 60;
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
>From 65ce6d57cb17abfe0226eb847dcfc98fe04518ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20C=C3=A1rdenas?= <miguelecsx at gmail.com>
Date: Sun, 13 Jul 2025 05:51:00 +0200
Subject: [PATCH 2/4] [llvm-advisor] Add utility for file and process
management
- Add FileManager for file operations.
- Add FileClassifier for compilation artifact categorization.
- Add ProcessRunner for subprocess execution.
---
.../llvm-advisor/src/Utils/FileClassifier.cpp | 136 ++++++++++++
.../llvm-advisor/src/Utils/FileClassifier.h | 26 +++
.../llvm-advisor/src/Utils/FileManager.cpp | 205 ++++++++++++++++++
.../llvm-advisor/src/Utils/FileManager.h | 46 ++++
.../llvm-advisor/src/Utils/ProcessRunner.cpp | 69 ++++++
.../llvm-advisor/src/Utils/ProcessRunner.h | 32 +++
6 files changed, 514 insertions(+)
create mode 100644 llvm/tools/llvm-advisor/src/Utils/FileClassifier.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Utils/FileClassifier.h
create mode 100644 llvm/tools/llvm-advisor/src/Utils/FileManager.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Utils/FileManager.h
create mode 100644 llvm/tools/llvm-advisor/src/Utils/ProcessRunner.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Utils/ProcessRunner.h
diff --git a/llvm/tools/llvm-advisor/src/Utils/FileClassifier.cpp b/llvm/tools/llvm-advisor/src/Utils/FileClassifier.cpp
new file mode 100644
index 0000000000000..e9b39f984c771
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/FileClassifier.cpp
@@ -0,0 +1,136 @@
+#include "FileClassifier.h"
+#include "llvm/Support/Path.h"
+
+namespace llvm {
+namespace advisor {
+
+FileClassification
+FileClassifier::classifyFile(const std::string &filePath) const {
+ StringRef filename = sys::path::filename(filePath);
+ StringRef extension = sys::path::extension(filePath);
+
+ FileClassification classification;
+ classification.isGenerated = true;
+ classification.isTemporary = false;
+
+ // LLVM IR files
+ if (extension == ".ll") {
+ classification.category = "ir";
+ classification.description = "LLVM IR text";
+ return classification;
+ }
+
+ // Assembly files
+ if (extension == ".s" || extension == ".S") {
+ classification.category = "assembly";
+ classification.description = "Assembly";
+ return classification;
+ }
+
+ // Optimization remarks
+ if (filename.ends_with(".opt.yaml") || filename.ends_with(".opt.yml")) {
+ classification.category = "remarks";
+ classification.description = "Optimization remarks";
+ return classification;
+ }
+
+ // Preprocessed files
+ if (extension == ".i" || extension == ".ii") {
+ classification.category = "preprocessed";
+ classification.description = "Preprocessed source";
+ return classification;
+ }
+
+ // AST dumps
+ if (extension == ".ast" || filename.contains("ast-dump")) {
+ classification.category = "ast";
+ classification.description = "AST dump";
+ return classification;
+ }
+
+ // Profile data
+ if (extension == ".profraw" || extension == ".profdata") {
+ classification.category = "profile";
+ classification.description = "Profile data";
+ return classification;
+ }
+
+ // Include trees
+ if (filename.contains(".include.") || filename.contains("include-tree")) {
+ classification.category = "include-tree";
+ classification.description = "Include tree";
+ return classification;
+ }
+
+ // Debug info
+ if (filename.contains("debug") || filename.contains("dwarf")) {
+ classification.category = "debug";
+ classification.description = "Debug information";
+ return classification;
+ }
+
+ // Static analyzer output
+ if (filename.contains("analysis") || filename.contains("analyzer")) {
+ classification.category = "static-analyzer";
+ classification.description = "Static analyzer output";
+ return classification;
+ }
+
+ // Macro expansion
+ if (filename.contains("macro-expanded")) {
+ classification.category = "macro-expansion";
+ classification.description = "Macro expansion";
+ return classification;
+ }
+
+ // Compilation phases
+ if (filename.contains("phases")) {
+ classification.category = "compilation-phases";
+ classification.description = "Compilation phases";
+ return classification;
+ }
+
+ // Control flow graph
+ if (extension == ".dot" || filename.contains("cfg")) {
+ classification.category = "cfg";
+ classification.description = "Control flow graph";
+ return classification;
+ }
+
+ // Template instantiation
+ if (filename.contains("template") || filename.contains("instantiation")) {
+ classification.category = "template-instantiation";
+ classification.description = "Template instantiation";
+ return classification;
+ }
+
+ // Default for unknown files
+ classification.category = "unknown";
+ classification.description = "Unknown file type";
+ classification.isGenerated = false;
+ return classification;
+}
+
+bool FileClassifier::shouldCollect(const std::string &filePath) const {
+ auto classification = classifyFile(filePath);
+ return classification.category != "unknown" && classification.isGenerated &&
+ !classification.isTemporary;
+}
+
+std::string FileClassifier::getLanguage(const std::string &filePath) const {
+ StringRef extension = sys::path::extension(filePath);
+
+ if (extension == ".c")
+ return "C";
+ if (extension == ".cpp" || extension == ".cc" || extension == ".cxx" ||
+ extension == ".C")
+ return "C++";
+ if (extension == ".h" || extension == ".hpp" || extension == ".hh" ||
+ extension == ".hxx")
+ return "Header";
+
+ return "Unknown";
+}
+
+} // namespace advisor
+} // namespace llvm
diff --git a/llvm/tools/llvm-advisor/src/Utils/FileClassifier.h b/llvm/tools/llvm-advisor/src/Utils/FileClassifier.h
new file mode 100644
index 0000000000000..6bf7c43ba4ffc
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/FileClassifier.h
@@ -0,0 +1,26 @@
+#ifndef LLVM_ADVISOR_FILE_CLASSIFIER_H
+#define LLVM_ADVISOR_FILE_CLASSIFIER_H
+
+#include <string>
+
+namespace llvm {
+namespace advisor {
+
+struct FileClassification {
+ std::string category;
+ std::string description;
+ bool isTemporary = false;
+ bool isGenerated = true;
+};
+
+class FileClassifier {
+public:
+ FileClassification classifyFile(const std::string &filePath) const;
+ bool shouldCollect(const std::string &filePath) const;
+ std::string getLanguage(const std::string &filePath) const;
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-advisor/src/Utils/FileManager.cpp b/llvm/tools/llvm-advisor/src/Utils/FileManager.cpp
new file mode 100644
index 0000000000000..7083d7edb7f3d
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/FileManager.cpp
@@ -0,0 +1,205 @@
+#include "FileManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#include <system_error>
+
+namespace llvm {
+namespace advisor {
+
+Expected<std::string> FileManager::createTempDir(const std::string &prefix) {
+ SmallString<128> tempDirPath;
+ if (std::error_code ec =
+ sys::fs::createUniqueDirectory(prefix, tempDirPath)) {
+ return createStringError(ec, "Failed to create unique temporary directory");
+ }
+ return std::string(tempDirPath.str());
+}
+
+Error FileManager::copyDirectory(const std::string &source,
+ const std::string &dest) {
+ std::error_code EC;
+
+ SmallString<128> sourcePathNorm(source);
+ // Remove trailing slash manually if present
+ if (sourcePathNorm.ends_with("/") && sourcePathNorm.size() > 1) {
+ sourcePathNorm.pop_back();
+ }
+
+ for (sys::fs::recursive_directory_iterator I(source, EC), E; I != E && !EC;
+ I.increment(EC)) {
+ StringRef currentPath = I->path();
+ SmallString<128> destPath(dest);
+
+ StringRef relativePath = currentPath;
+ if (!relativePath.consume_front(sourcePathNorm)) {
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Path '" + currentPath.str() + "' not in source dir '" + source +
+ "'");
+ }
+ // Remove leading slash manually if present
+ if (relativePath.starts_with("/")) {
+ relativePath = relativePath.drop_front(1);
+ }
+
+ sys::path::append(destPath, relativePath);
+
+ if (sys::fs::is_directory(currentPath)) {
+ if (sys::fs::create_directories(destPath)) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to create directory: " +
+ destPath.str().str());
+ }
+ } else {
+ if (sys::fs::create_directories(sys::path::parent_path(destPath))) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to create parent directory for: " +
+ destPath.str().str());
+ }
+ if (sys::fs::copy_file(currentPath, destPath)) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to copy file: " + currentPath.str());
+ }
+ }
+ }
+
+ if (EC) {
+ return createStringError(EC, "Failed to iterate directory: " + source);
+ }
+
+ return Error::success();
+}
+
+Error FileManager::removeDirectory(const std::string &path) {
+ if (!sys::fs::exists(path)) {
+ return Error::success();
+ }
+
+ std::error_code EC;
+ std::vector<std::string> Dirs;
+ for (sys::fs::recursive_directory_iterator I(path, EC), E; I != E && !EC;
+ I.increment(EC)) {
+ if (I->type() == sys::fs::file_type::directory_file) {
+ Dirs.push_back(I->path());
+ } else {
+ if (auto E = sys::fs::remove(I->path())) {
+ return createStringError(E, "Failed to remove file: " + I->path());
+ }
+ }
+ }
+
+ if (EC) {
+ return createStringError(EC, "Error iterating directory " + path);
+ }
+
+ for (const auto &Dir : llvm::reverse(Dirs)) {
+ if (auto E = sys::fs::remove(Dir)) {
+ return createStringError(E, "Failed to remove directory: " + Dir);
+ }
+ }
+
+ if (auto E = sys::fs::remove(path)) {
+ return createStringError(E,
+ "Failed to remove top-level directory: " + path);
+ }
+
+ return Error::success();
+}
+
+std::vector<std::string> FileManager::findFiles(const std::string &directory,
+ const std::string &pattern) {
+ std::vector<std::string> files;
+ std::error_code EC;
+ for (sys::fs::recursive_directory_iterator I(directory, EC), E; I != E && !EC;
+ I.increment(EC)) {
+ if (I->type() != sys::fs::file_type::directory_file) {
+ StringRef filename = sys::path::filename(I->path());
+ if (filename.find(pattern) != StringRef::npos) {
+ files.push_back(I->path());
+ }
+ }
+ }
+ return files;
+}
+
+std::vector<std::string>
+FileManager::findFilesByExtension(const std::string &directory,
+ const std::vector<std::string> &extensions) {
+ std::vector<std::string> files;
+ std::error_code EC;
+ for (sys::fs::recursive_directory_iterator I(directory, EC), E; I != E && !EC;
+ I.increment(EC)) {
+ if (I->type() != sys::fs::file_type::directory_file) {
+ StringRef filepath = I->path();
+ for (const auto &ext : extensions) {
+ if (filepath.ends_with(ext)) {
+ files.push_back(filepath.str());
+ break;
+ }
+ }
+ }
+ }
+ return files;
+}
+
+Error FileManager::moveFile(const std::string &source,
+ const std::string &dest) {
+ if (source == dest) {
+ return Error::success();
+ }
+
+ if (sys::fs::create_directories(sys::path::parent_path(dest))) {
+ return createStringError(
+ std::make_error_code(std::errc::io_error),
+ "Failed to create parent directory for destination: " + dest);
+ }
+
+ if (sys::fs::rename(source, dest)) {
+ // If rename fails, try copy and remove
+ if (sys::fs::copy_file(source, dest)) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to move file (copy failed): " + source);
+ }
+ if (sys::fs::remove(source)) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to move file (source removal failed): " +
+ source);
+ }
+ }
+
+ return Error::success();
+}
+
+Error FileManager::copyFile(const std::string &source,
+ const std::string &dest) {
+ if (source == dest) {
+ return Error::success();
+ }
+
+ if (sys::fs::create_directories(sys::path::parent_path(dest))) {
+ return createStringError(
+ std::make_error_code(std::errc::io_error),
+ "Failed to create parent directory for destination: " + dest);
+ }
+
+ if (sys::fs::copy_file(source, dest)) {
+ return createStringError(std::make_error_code(std::errc::io_error),
+ "Failed to copy file: " + source);
+ }
+
+ return Error::success();
+}
+
+Expected<size_t> FileManager::getFileSize(const std::string &path) {
+ sys::fs::file_status status;
+ if (auto EC = sys::fs::status(path, status)) {
+ return createStringError(EC, "File not found: " + path);
+ }
+
+ return status.getSize();
+}
+
+} // namespace advisor
+} // namespace llvm
\ No newline at end of file
diff --git a/llvm/tools/llvm-advisor/src/Utils/FileManager.h b/llvm/tools/llvm-advisor/src/Utils/FileManager.h
new file mode 100644
index 0000000000000..07b49e647f542
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/FileManager.h
@@ -0,0 +1,46 @@
+#ifndef LLVM_ADVISOR_FILE_MANAGER_H
+#define LLVM_ADVISOR_FILE_MANAGER_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace advisor {
+
+class FileManager {
+public:
+ /// Create unique temporary directory with pattern llvm-advisor-xxxxx
+ static Expected<std::string>
+ createTempDir(const std::string &prefix = "llvm-advisor");
+
+ /// Recursively copy directory
+ static Error copyDirectory(const std::string &source,
+ const std::string &dest);
+
+ /// Remove directory and contents
+ static Error removeDirectory(const std::string &path);
+
+ /// Find files matching pattern
+ static std::vector<std::string> findFiles(const std::string &directory,
+ const std::string &pattern);
+
+ /// Find files by extension
+ static std::vector<std::string>
+ findFilesByExtension(const std::string &directory,
+ const std::vector<std::string> &extensions);
+
+ /// Move file from source to destination
+ static Error moveFile(const std::string &source, const std::string &dest);
+
+ /// Copy file from source to destination
+ static Error copyFile(const std::string &source, const std::string &dest);
+
+ /// Get file size
+ static Expected<size_t> getFileSize(const std::string &path);
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.cpp b/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.cpp
new file mode 100644
index 0000000000000..b08b3cc88a434
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.cpp
@@ -0,0 +1,69 @@
+#include "ProcessRunner.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+
+namespace llvm {
+namespace advisor {
+
+Expected<ProcessRunner::ProcessResult>
+ProcessRunner::run(const std::string &program,
+ const std::vector<std::string> &args, int timeoutSeconds) {
+
+ auto programPath = sys::findProgramByName(program);
+ if (!programPath) {
+ return createStringError(programPath.getError(),
+ "Tool not found: " + program);
+ }
+
+ std::vector<StringRef> execArgs;
+ execArgs.push_back(program);
+ for (const auto &arg : args) {
+ execArgs.push_back(arg);
+ }
+
+ SmallString<128> stdoutPath, stderrPath;
+ sys::fs::createTemporaryFile("stdout", "tmp", stdoutPath);
+ sys::fs::createTemporaryFile("stderr", "tmp", stderrPath);
+
+ std::optional<StringRef> redirects[] = {
+ std::nullopt, // stdin
+ StringRef(stdoutPath), // stdout
+ StringRef(stderrPath) // stderr
+ };
+
+ int exitCode = sys::ExecuteAndWait(*programPath, execArgs, std::nullopt,
+ redirects, timeoutSeconds);
+
+ ProcessResult result;
+ result.exitCode = exitCode;
+ // TODO: Collect information about compilation time
+ result.executionTime = 0; // not tracking time
+
+ auto stdoutBuffer = MemoryBuffer::getFile(stdoutPath);
+ if (stdoutBuffer) {
+ result.stdout = (*stdoutBuffer)->getBuffer().str();
+ }
+
+ auto stderrBuffer = MemoryBuffer::getFile(stderrPath);
+ if (stderrBuffer) {
+ result.stderr = (*stderrBuffer)->getBuffer().str();
+ }
+
+ sys::fs::remove(stdoutPath);
+ sys::fs::remove(stderrPath);
+
+ return result;
+}
+
+Expected<ProcessRunner::ProcessResult> ProcessRunner::runWithEnv(
+ const std::string &program, const std::vector<std::string> &args,
+ const std::vector<std::string> &env, int timeoutSeconds) {
+
+ // For simplicity, just use the regular run method
+ // Environment variables can be added later if needed
+ return run(program, args, timeoutSeconds);
+}
+
+} // namespace advisor
+} // namespace llvm
diff --git a/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.h b/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.h
new file mode 100644
index 0000000000000..ffd0ef353ba16
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Utils/ProcessRunner.h
@@ -0,0 +1,32 @@
+#ifndef LLVM_ADVISOR_PROCESS_RUNNER_H
+#define LLVM_ADVISOR_PROCESS_RUNNER_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace advisor {
+
+class ProcessRunner {
+public:
+ struct ProcessResult {
+ int exitCode;
+ std::string stdout;
+ std::string stderr;
+ double executionTime;
+ };
+
+ static Expected<ProcessResult> run(const std::string &program,
+ const std::vector<std::string> &args,
+ int timeoutSeconds = 60);
+
+ static Expected<ProcessResult>
+ runWithEnv(const std::string &program, const std::vector<std::string> &args,
+ const std::vector<std::string> &env, int timeoutSeconds = 60);
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
>From cc3e1ed4848ff6b7753cf731779602e4e7593309 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20C=C3=A1rdenas?= <miguelecsx at gmail.com>
Date: Sun, 13 Jul 2025 06:09:39 +0200
Subject: [PATCH 3/4] [llvm-advisor] Add basic build/compilation data models
Introduce data structures that represent a single build phase
and compilation unit.
---
.../llvm-advisor/src/Core/BuildContext.h | 52 +++++++++++++++
.../llvm-advisor/src/Core/CompilationUnit.cpp | 66 +++++++++++++++++++
.../llvm-advisor/src/Core/CompilationUnit.h | 58 ++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 llvm/tools/llvm-advisor/src/Core/BuildContext.h
create mode 100644 llvm/tools/llvm-advisor/src/Core/CompilationUnit.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Core/CompilationUnit.h
diff --git a/llvm/tools/llvm-advisor/src/Core/BuildContext.h b/llvm/tools/llvm-advisor/src/Core/BuildContext.h
new file mode 100644
index 0000000000000..4f40c37ca8706
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Core/BuildContext.h
@@ -0,0 +1,52 @@
+#ifndef LLVM_ADVISOR_BUILD_CONTEXT_H
+#define LLVM_ADVISOR_BUILD_CONTEXT_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace advisor {
+
+enum class BuildPhase {
+ Unknown,
+ Preprocessing,
+ Compilation,
+ Assembly,
+ Linking,
+ Archiving,
+ CMakeConfigure,
+ CMakeBuild,
+ MakefileBuild
+};
+
+enum class BuildTool {
+ Unknown,
+ Clang,
+ GCC,
+ LLVM_Tools,
+ CMake,
+ Make,
+ Ninja,
+ Linker,
+ Archiver
+};
+
+struct BuildContext {
+ BuildPhase phase;
+ BuildTool tool;
+ std::string workingDirectory;
+ std::string outputDirectory;
+ std::vector<std::string> inputFiles;
+ std::vector<std::string> outputFiles;
+ std::vector<std::string> expectedGeneratedFiles;
+ std::map<std::string, std::string> metadata;
+ bool hasOffloading = false;
+ bool hasDebugInfo = false;
+ bool hasOptimization = false;
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
diff --git a/llvm/tools/llvm-advisor/src/Core/CompilationUnit.cpp b/llvm/tools/llvm-advisor/src/Core/CompilationUnit.cpp
new file mode 100644
index 0000000000000..8b6a478cfaf63
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Core/CompilationUnit.cpp
@@ -0,0 +1,66 @@
+#include "CompilationUnit.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace llvm {
+namespace advisor {
+
+CompilationUnit::CompilationUnit(const CompilationUnitInfo &info,
+ const std::string &workDir)
+ : info_(info), workDir_(workDir) {
+ // Create unit-specific data directory
+ SmallString<128> dataDir;
+ sys::path::append(dataDir, workDir, "units", info.name);
+ sys::fs::create_directories(dataDir);
+}
+
+std::string CompilationUnit::getPrimarySource() const {
+ if (info_.sources.empty()) {
+ return "";
+ }
+ return info_.sources[0].path;
+}
+
+std::string CompilationUnit::getDataDir() const {
+ SmallString<128> dataDir;
+ sys::path::append(dataDir, workDir_, "units", info_.name);
+ return dataDir.str().str();
+}
+
+std::string CompilationUnit::getExecutablePath() const {
+ return info_.outputExecutable;
+}
+
+void CompilationUnit::addGeneratedFile(const std::string &type,
+ const std::string &path) {
+ generatedFiles_[type].push_back(path);
+}
+
+bool CompilationUnit::hasGeneratedFiles(const std::string &type) const {
+ if (type.empty()) {
+ return !generatedFiles_.empty();
+ }
+ auto it = generatedFiles_.find(type);
+ return it != generatedFiles_.end() && !it->second.empty();
+}
+
+std::vector<std::string>
+CompilationUnit::getGeneratedFiles(const std::string &type) const {
+ if (type.empty()) {
+ std::vector<std::string> allFiles;
+ for (const auto &pair : generatedFiles_) {
+ allFiles.insert(allFiles.end(), pair.second.begin(), pair.second.end());
+ }
+ return allFiles;
+ }
+ auto it = generatedFiles_.find(type);
+ return it != generatedFiles_.end() ? it->second : std::vector<std::string>();
+}
+
+const std::unordered_map<std::string, std::vector<std::string>> &
+CompilationUnit::getAllGeneratedFiles() const {
+ return generatedFiles_;
+}
+
+} // namespace advisor
+} // namespace llvm
\ No newline at end of file
diff --git a/llvm/tools/llvm-advisor/src/Core/CompilationUnit.h b/llvm/tools/llvm-advisor/src/Core/CompilationUnit.h
new file mode 100644
index 0000000000000..18dbc35ab5aec
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Core/CompilationUnit.h
@@ -0,0 +1,58 @@
+#ifndef LLVM_ADVISOR_COMPILATION_UNIT_H
+#define LLVM_ADVISOR_COMPILATION_UNIT_H
+
+#include "llvm/Support/Error.h"
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace llvm {
+namespace advisor {
+
+struct SourceFile {
+ std::string path;
+ std::string language;
+ bool isHeader = false;
+ std::vector<std::string> dependencies;
+};
+
+struct CompilationUnitInfo {
+ std::string name;
+ std::vector<SourceFile> sources;
+ std::vector<std::string> compileFlags;
+ std::string targetArch;
+ bool hasOffloading = false;
+ std::string outputObject;
+ std::string outputExecutable;
+};
+
+class CompilationUnit {
+public:
+ CompilationUnit(const CompilationUnitInfo &info, const std::string &workDir);
+
+ const std::string &getName() const { return info_.name; }
+ const CompilationUnitInfo &getInfo() const { return info_; }
+ const std::string &getWorkDir() const { return workDir_; }
+ std::string getPrimarySource() const;
+
+ std::string getDataDir() const;
+ std::string getExecutablePath() const;
+
+ void addGeneratedFile(const std::string &type, const std::string &path);
+
+ bool hasGeneratedFiles(const std::string &type) const;
+ std::vector<std::string>
+ getGeneratedFiles(const std::string &type = "") const;
+ const std::unordered_map<std::string, std::vector<std::string>> &
+ getAllGeneratedFiles() const;
+
+private:
+ CompilationUnitInfo info_;
+ std::string workDir_;
+ std::unordered_map<std::string, std::vector<std::string>> generatedFiles_;
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
\ No newline at end of file
>From 336db515d67b787c3e63d33e77f2c70787f1a578 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20C=C3=A1rdenas?= <miguelecsx at gmail.com>
Date: Sun, 13 Jul 2025 06:18:52 +0200
Subject: [PATCH 4/4] [llvm-advisor] Add command analyzer helper
CommandAnalyzer inspects an incoming compiler or build-system
invocation and classifies the tool in use, the build phase,
input/output files and notable flags.
---
.../llvm-advisor/src/Core/CommandAnalyzer.cpp | 167 ++++++++++++++++++
.../llvm-advisor/src/Core/CommandAnalyzer.h | 32 ++++
2 files changed, 199 insertions(+)
create mode 100644 llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.cpp
create mode 100644 llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.h
diff --git a/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.cpp b/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.cpp
new file mode 100644
index 0000000000000..3192c42669e65
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.cpp
@@ -0,0 +1,167 @@
+#include "CommandAnalyzer.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace llvm {
+namespace advisor {
+
+CommandAnalyzer::CommandAnalyzer(const std::string &command,
+ const std::vector<std::string> &args)
+ : command_(command), args_(args) {}
+
+BuildContext CommandAnalyzer::analyze() const {
+ BuildContext context;
+ SmallString<256> cwd;
+ sys::fs::current_path(cwd);
+ context.workingDirectory = cwd.str().str();
+
+ context.tool = detectBuildTool();
+ context.phase = detectBuildPhase(context.tool);
+ context.inputFiles = extractInputFiles();
+ context.outputFiles = extractOutputFiles();
+ detectBuildFeatures(context);
+
+ return context;
+}
+
+BuildTool CommandAnalyzer::detectBuildTool() const {
+ return StringSwitch<BuildTool>(sys::path::filename(command_))
+ .StartsWith("clang", BuildTool::Clang)
+ .StartsWith("gcc", BuildTool::GCC)
+ .StartsWith("g++", BuildTool::GCC)
+ .Case("cmake", BuildTool::CMake)
+ .Case("make", BuildTool::Make)
+ .Case("ninja", BuildTool::Ninja)
+ .EndsWith("-ld", BuildTool::Linker)
+ .Case("ld", BuildTool::Linker)
+ .Case("ar", BuildTool::Archiver)
+ .Case("llvm-ar", BuildTool::Archiver)
+ .StartsWith("llvm-", BuildTool::LLVM_Tools)
+ .Default(BuildTool::Unknown);
+}
+
+BuildPhase CommandAnalyzer::detectBuildPhase(BuildTool tool) const {
+ if (tool == BuildTool::CMake) {
+ for (const auto &arg : args_) {
+ if (arg == "--build")
+ return BuildPhase::CMakeBuild;
+ }
+ return BuildPhase::CMakeConfigure;
+ }
+
+ if (tool == BuildTool::Make || tool == BuildTool::Ninja) {
+ return BuildPhase::MakefileBuild;
+ }
+
+ if (tool == BuildTool::Linker) {
+ return BuildPhase::Linking;
+ }
+
+ if (tool == BuildTool::Archiver) {
+ return BuildPhase::Archiving;
+ }
+
+ if (tool == BuildTool::Clang || tool == BuildTool::GCC) {
+ for (const auto &arg : args_) {
+ if (arg == "-E")
+ return BuildPhase::Preprocessing;
+ if (arg == "-S")
+ return BuildPhase::Assembly;
+ if (arg == "-c")
+ return BuildPhase::Compilation;
+ }
+
+ bool hasObjectFile = false;
+ for (const auto &Arg : args_) {
+ StringRef argRef(Arg);
+ if (argRef.ends_with(".o") || argRef.ends_with(".O") ||
+ argRef.ends_with(".obj") || argRef.ends_with(".OBJ")) {
+ hasObjectFile = true;
+ break;
+ }
+ }
+ if (hasObjectFile) {
+ return BuildPhase::Linking;
+ }
+
+ bool hasSourceFile = false;
+ for (const auto &Arg : args_) {
+ StringRef argRef(Arg);
+ if (argRef.ends_with(".c") || argRef.ends_with(".C") ||
+ argRef.ends_with(".cpp") || argRef.ends_with(".CPP") ||
+ argRef.ends_with(".cc") || argRef.ends_with(".CC") ||
+ argRef.ends_with(".cxx") || argRef.ends_with(".CXX")) {
+ hasSourceFile = true;
+ break;
+ }
+ }
+ if (hasSourceFile) {
+ return BuildPhase::Compilation; // Default for source files
+ }
+ }
+
+ return BuildPhase::Unknown;
+}
+
+void CommandAnalyzer::detectBuildFeatures(BuildContext &context) const {
+ for (const auto &arg : args_) {
+ if (arg == "-g" || StringRef(arg).starts_with("-g")) {
+ context.hasDebugInfo = true;
+ }
+
+ if (StringRef(arg).starts_with("-O") && arg.length() > 2) {
+ context.hasOptimization = true;
+ }
+
+ if (arg.find("openmp") != std::string::npos ||
+ arg.find("openacc") != std::string::npos ||
+ arg.find("cuda") != std::string::npos ||
+ arg.find("offload") != std::string::npos) {
+ context.hasOffloading = true;
+ }
+
+ if (StringRef(arg).starts_with("-march=")) {
+ context.metadata["target_arch"] = arg.substr(7);
+ }
+ if (StringRef(arg).starts_with("-mtune=")) {
+ context.metadata["tune"] = arg.substr(7);
+ }
+ if (StringRef(arg).starts_with("--offload-arch=")) {
+ context.metadata["offload_arch"] = arg.substr(15);
+ }
+ }
+}
+
+std::vector<std::string> CommandAnalyzer::extractInputFiles() const {
+ std::vector<std::string> inputs;
+ for (size_t i = 0; i < args_.size(); ++i) {
+ const auto &arg = args_[i];
+ if (StringRef(arg).starts_with("-")) {
+ if (arg == "-o" || arg == "-I" || arg == "-L" || arg == "-D") {
+ i++;
+ }
+ continue;
+ }
+ if (sys::fs::exists(arg)) {
+ inputs.push_back(arg);
+ }
+ }
+ return inputs;
+}
+
+std::vector<std::string> CommandAnalyzer::extractOutputFiles() const {
+ std::vector<std::string> outputs;
+ for (size_t i = 0; i < args_.size(); ++i) {
+ const auto &arg = args_[i];
+ if (arg == "-o" && i + 1 < args_.size()) {
+ outputs.push_back(args_[i + 1]);
+ i++;
+ }
+ }
+ return outputs;
+}
+
+} // namespace advisor
+} // namespace llvm
diff --git a/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.h b/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.h
new file mode 100644
index 0000000000000..c3efdff147e5f
--- /dev/null
+++ b/llvm/tools/llvm-advisor/src/Core/CommandAnalyzer.h
@@ -0,0 +1,32 @@
+#ifndef LLVM_ADVISOR_COMMAND_ANALYZER_H
+#define LLVM_ADVISOR_COMMAND_ANALYZER_H
+
+#include "BuildContext.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace advisor {
+
+class CommandAnalyzer {
+public:
+ CommandAnalyzer(const std::string &command,
+ const std::vector<std::string> &args);
+
+ BuildContext analyze() const;
+
+private:
+ BuildTool detectBuildTool() const;
+ BuildPhase detectBuildPhase(BuildTool tool) const;
+ void detectBuildFeatures(BuildContext &context) const;
+ std::vector<std::string> extractInputFiles() const;
+ std::vector<std::string> extractOutputFiles() const;
+
+ std::string command_;
+ std::vector<std::string> args_;
+};
+
+} // namespace advisor
+} // namespace llvm
+
+#endif
More information about the llvm-commits
mailing list