[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:13:42 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/3] [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/3] [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/3] [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



More information about the llvm-commits mailing list