[llvm] [BOLT] Add bolt-rt to runtimes (PR #187308)

Casey Smalley via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 16 05:50:58 PDT 2026


https://github.com/CaseySmalley updated https://github.com/llvm/llvm-project/pull/187308

>From a359cd327495a1731a6cf2da77261217572b4cf2 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Wed, 18 Mar 2026 13:36:47 +0000
Subject: [PATCH 01/13] [BOLT] Add bolt-rt to runtimes

Separate bolt-rt into the runtimes build. Bolt now looks for libraries
based on a triple.

This enables using bolt to instrument binaries across different
architectures.

[BOLT] tmp move

[BOLT] tmp move back
---
 bolt-rt/CMakeLists.txt                  | 114 ++++++++++++++++++++++++
 {bolt/runtime => bolt-rt}/common.h      |   0
 {bolt/runtime => bolt-rt}/config.h.in   |   0
 {bolt/runtime => bolt-rt}/hugify.cpp    |   0
 {bolt/runtime => bolt-rt}/instr.cpp     |   0
 {bolt/runtime => bolt-rt}/sys_aarch64.h |   0
 {bolt/runtime => bolt-rt}/sys_riscv64.h |   0
 {bolt/runtime => bolt-rt}/sys_x86_64.h  |   0
 bolt/CMakeLists.txt                     |  46 +---------
 bolt/lib/RuntimeLibs/RuntimeLibrary.cpp |  12 +++
 bolt/runtime/CMakeLists.txt             |  86 ------------------
 bolt/tools/driver/CMakeLists.txt        |  15 +---
 llvm/CMakeLists.txt                     |   7 +-
 runtimes/CMakeLists.txt                 |   2 +-
 14 files changed, 138 insertions(+), 144 deletions(-)
 create mode 100644 bolt-rt/CMakeLists.txt
 rename {bolt/runtime => bolt-rt}/common.h (100%)
 rename {bolt/runtime => bolt-rt}/config.h.in (100%)
 rename {bolt/runtime => bolt-rt}/hugify.cpp (100%)
 rename {bolt/runtime => bolt-rt}/instr.cpp (100%)
 rename {bolt/runtime => bolt-rt}/sys_aarch64.h (100%)
 rename {bolt/runtime => bolt-rt}/sys_riscv64.h (100%)
 rename {bolt/runtime => bolt-rt}/sys_x86_64.h (100%)
 delete mode 100644 bolt/runtime/CMakeLists.txt

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
new file mode 100644
index 0000000000000..2d7c3101d3dd1
--- /dev/null
+++ b/bolt-rt/CMakeLists.txt
@@ -0,0 +1,114 @@
+cmake_minimum_required(VERSION 3.20.0)
+
+include(CheckCXXCompilerFlag)
+include(CheckIncludeFiles)
+include(CMakeParseArguments)
+include(GNUInstallDirs)
+
+# Is this built via -DLLVM_ENABLE_RUNTIMES="bolt-rt"?.
+if(LLVM_RUNTIMES_BUILD)
+  set(LLVM_SUBPROJECT_TITLE "Bolt-RT")
+# Is this being built directly?
+elseif(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+  project(bolt-rt)
+endif()
+
+# Use LLVM_RUNTIMES_TARGET when built under runtimes.
+if(LLVM_RUNTIMES_BUILD)
+  set(BOLT_RT_TARGET_TRIPLE "${LLVM_RUNTIMES_TARGET}")
+endif()
+
+if(NOT BOLT_RT_TARGET_TRIPLE)
+  message(STATUS "bolt_rt: BOLT_RT_TARGET_TRIPLE is unset, building for the host.")
+  get_host_triple(BOLT_RT_TARGET_TRIPLE)
+endif()
+
+message(STATUS "bolt-rt: targeting ${BOLT_RT_TARGET_TRIPLE}")
+
+# Add custom target for runtime builds.
+if(LLVM_RUNTIMES_BUILD)
+  add_custom_target(bolt-rt)
+  set_target_properties(bolt-rt PROPERTIES FOLDER "BOLT-RT/Metatargets")
+  add_llvm_install_targets(install-bolt-rt
+                           DEPENDS bolt-rt
+                           COMPONENTS bolt-rt)
+endif()
+
+# For runtime builds, the installation directory is already known,
+# otherwise assume lib.
+if(LLVM_RUNTIMES_BUILD)
+  set(BOLT_RT_LIBRARY_DIR "${LLVM_LIBRARY_DIR}")
+else()
+  set(BOLT_RT_LIBRARY_DIR "lib${LLVM_LIBDIR_SUFFIX}")
+endif()
+
+if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
+  string(APPEND BOLT_RT_LIBRARY_DIR "/${BOLT_RT_TARGET_TRIPLE}")
+  set(BOLT_RT_ARCHIVE_SUFFIX "")
+else()
+  # TODO(casey.smalley at arm.com): Does this need to account for
+  # LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF?
+  message(FATAL_ERROR "bolt_rt: Not implemented.")
+endif()
+
+# Build flags.
+list(APPEND BOLT_RT_FLAGS
+    -ffreestanding
+    -fno-exceptions
+    -fno-rtti
+    -fno-stack-protector
+    -fPIC
+    # Runtime currently assumes omitted frame pointers for functions marked __attribute((naked)).
+    # Protect against distros adding -fno-omit-frame-pointer and compiling with GCC.
+    # Refs: llvm/llvm-project#148595 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77882
+    -fomit-frame-pointer)
+
+if(BOLT_RT_TARGET_TRIPLE MATCHES "x86_64")
+  list(APPEND BOLT_RT_FLAGS
+       -mno-sse
+       -mgeneral-regs-only)
+elseif(BOLT_RT_TARGET_TRIPLE MATCHES "aarch64")
+  check_cxx_compiler_flag("-mno-outline-atomics" CXX_SUPPORTS_OUTLINE_ATOMICS)
+
+  if(CXX_SUPPORTS_OUTLINE_ATOMICS)
+    list(APPEND BOLT_RT_FLAGS
+         -mno-outline-atomics
+         -mgeneral-regs-only)
+  endif()
+endif()
+
+# Build libraries.
+check_include_files(elf.h HAVE_ELF_H)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/config.h")
+
+function(add_bolt_rt_library)
+  cmake_parse_arguments(ARG "" "TARGET" "SOURCES;FLAGS" ${ARGN})
+  add_library(${ARG_TARGET} ${ARG_SOURCES})
+
+  # Don't let the compiler think it can create calls to standard libs
+  target_include_directories(${ARG_TARGET}
+                             PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
+
+  target_compile_options(${ARG_TARGET}
+                         PRIVATE ${BOLT_RT_FLAGS} ${ARG_FLAGS})
+
+  # Ouput archives into a per-triple path or with a per triple suffix.
+  set_target_properties(${ARG_TARGET} PROPERTIES
+                        ARCHIVE_OUTPUT_DIRECTORY "${BOLT_RT_LIBRARY_DIR}"
+                        ARCHIVE_OUTPUT_NAME "${ARG_TARGET}${BOLT_RT_ARCHIVE_SUFFIX}")
+
+  if(LLVM_RUNTIMES_BUILD)
+    add_dependencies(bolt-rt ${ARG_TARGET})
+  endif()
+
+  # Install to a matching directory.
+  install(TARGETS ${ARG_TARGET}
+          DESTINATION "${BOLT_RT_LIBRARY_DIR}")
+endfunction()
+
+add_bolt_rt_library(TARGET bolt_rt_instr
+                    SOURCES instr.cpp)
+
+add_bolt_rt_library(TARGET bolt_rt_hugify
+                    SOURCES hugify.cpp)
diff --git a/bolt/runtime/common.h b/bolt-rt/common.h
similarity index 100%
rename from bolt/runtime/common.h
rename to bolt-rt/common.h
diff --git a/bolt/runtime/config.h.in b/bolt-rt/config.h.in
similarity index 100%
rename from bolt/runtime/config.h.in
rename to bolt-rt/config.h.in
diff --git a/bolt/runtime/hugify.cpp b/bolt-rt/hugify.cpp
similarity index 100%
rename from bolt/runtime/hugify.cpp
rename to bolt-rt/hugify.cpp
diff --git a/bolt/runtime/instr.cpp b/bolt-rt/instr.cpp
similarity index 100%
rename from bolt/runtime/instr.cpp
rename to bolt-rt/instr.cpp
diff --git a/bolt/runtime/sys_aarch64.h b/bolt-rt/sys_aarch64.h
similarity index 100%
rename from bolt/runtime/sys_aarch64.h
rename to bolt-rt/sys_aarch64.h
diff --git a/bolt/runtime/sys_riscv64.h b/bolt-rt/sys_riscv64.h
similarity index 100%
rename from bolt/runtime/sys_riscv64.h
rename to bolt-rt/sys_riscv64.h
diff --git a/bolt/runtime/sys_x86_64.h b/bolt-rt/sys_x86_64.h
similarity index 100%
rename from bolt/runtime/sys_x86_64.h
rename to bolt-rt/sys_x86_64.h
diff --git a/bolt/CMakeLists.txt b/bolt/CMakeLists.txt
index 5c7d51e1e398c..4458c237ebd9f 100644
--- a/bolt/CMakeLists.txt
+++ b/bolt/CMakeLists.txt
@@ -94,16 +94,6 @@ if ((CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"
   set(BOLT_ENABLE_RUNTIME_default ON)
 endif()
 option(BOLT_ENABLE_RUNTIME "Enable BOLT runtime" ${BOLT_ENABLE_RUNTIME_default})
-if (BOLT_ENABLE_RUNTIME)
-  # Some systems prevent reading /proc/self/map_files
-  execute_process(COMMAND ls /proc/self/map_files
-    RESULT_VARIABLE LS OUTPUT_QUIET ERROR_QUIET)
-  if (LS)
-    message(WARNING
-      "BOLT runtime may not be able to read /proc/self/map_files. Please use
-      `--instrumentation-binpath <path-to-instrumented-binary>` option.")
-  endif()
-endif()
 
 set(BOLT_CLANG_EXE "" CACHE FILEPATH "Path to clang executable for the target \
 architecture for use in BOLT tests")
@@ -140,38 +130,6 @@ if (LLVM_INCLUDE_TESTS)
   endif()
 endif()
 
-if (BOLT_ENABLE_RUNTIME)
-  message(STATUS "Building BOLT runtime libraries for ${CMAKE_SYSTEM_PROCESSOR}")
-  set(extra_args "")
-  if(CMAKE_SYSROOT)
-    list(APPEND extra_args -DCMAKE_SYSROOT=${CMAKE_SYSROOT})
-  endif()
-
-  include(ExternalProject)
-  ExternalProject_Add(bolt_rt
-    SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/runtime"
-    STAMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-stamps
-    BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-bins
-    CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-               -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-               -DCMAKE_BUILD_TYPE=Release
-               -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
-               -DLLVM_LIBDIR_SUFFIX=${LLVM_LIBDIR_SUFFIX}
-               -DLLVM_LIBRARY_DIR=${LLVM_LIBRARY_DIR}
-               -DBOLT_BUILT_STANDALONE=${BOLT_BUILT_STANDALONE}
-               ${extra_args}
-    INSTALL_COMMAND ""
-    BUILD_ALWAYS True
-    )
-  install(CODE "execute_process\(COMMAND \${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=\${CMAKE_INSTALL_PREFIX} -P ${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-bins/cmake_install.cmake \)"
-    COMPONENT bolt)
-  add_llvm_install_targets(install-bolt_rt
-    DEPENDS bolt_rt bolt
-    COMPONENT bolt)
-  set(LIBBOLT_RT_INSTR "${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-bins/lib${LLVM_LIBDIR_SUFFIX}/libbolt_rt_instr.a")
-  set(LIBBOLT_RT_HUGIFY "${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-bins/lib${LLVM_LIBDIR_SUFFIX}/libbolt_rt_hugify.a")
-endif()
-
 find_program(GNU_LD_EXECUTABLE NAMES ${LLVM_DEFAULT_TARGET_TRIPLE}-ld.bfd ld.bfd DOC "GNU ld")
 
 include(AddBOLT)
@@ -183,6 +141,10 @@ add_custom_target(bolt)
 set_target_properties(bolt PROPERTIES FOLDER "BOLT/Metatargets")
 add_llvm_install_targets(install-bolt DEPENDS bolt COMPONENT bolt)
 
+if (bolt-rt IN_LIST LLVM_ENABLE_RUNTIMES)
+  add_dependencies(bolt bolt-rt)
+endif()
+
 include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/include
   ${CMAKE_CURRENT_BINARY_DIR}/include
diff --git a/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp b/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
index 98852ee691ceb..51f134e22e8e3 100644
--- a/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
+++ b/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
@@ -13,10 +13,14 @@
 #include "bolt/RuntimeLibs/RuntimeLibrary.h"
 #include "bolt/Core/Linker.h"
 #include "bolt/RuntimeLibs/RuntimeLibraryVariables.inc"
+#include "bolt/Utils/CommandLineOpts.h"
 #include "bolt/Utils/Utils.h"
 #include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Config/llvm-config.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
 
@@ -25,6 +29,12 @@
 using namespace llvm;
 using namespace bolt;
 
+namespace opts {
+static cl::opt<std::string> TargetTriple(
+    "target", cl::desc("Target triple used to select runtime libraries."),
+    cl::init(LLVM_DEFAULT_TARGET_TRIPLE), cl::cat(BoltOptCategory));
+} // namespace opts
+
 void RuntimeLibrary::anchor() {}
 
 std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
@@ -32,6 +42,7 @@ std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
   StringRef Dir = llvm::sys::path::parent_path(ToolPath);
   SmallString<128> LibPath = llvm::sys::path::parent_path(Dir);
   llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
+  llvm::sys::path::append(LibPath, opts::TargetTriple);
   if (!llvm::sys::fs::exists(LibPath)) {
     // In some cases we install bolt binary into one level deeper in bin/,
     // we need to go back one more level to find lib directory.
@@ -61,6 +72,7 @@ std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
 
 std::string RuntimeLibrary::getLibPathByInstalled(StringRef LibFileName) {
   SmallString<128> LibPath(CMAKE_INSTALL_FULL_LIBDIR);
+  llvm::sys::path::append(LibPath, opts::TargetTriple);
   llvm::sys::path::append(LibPath, LibFileName);
   return std::string(LibPath);
 }
diff --git a/bolt/runtime/CMakeLists.txt b/bolt/runtime/CMakeLists.txt
deleted file mode 100644
index 63f178bd263c2..0000000000000
--- a/bolt/runtime/CMakeLists.txt
+++ /dev/null
@@ -1,86 +0,0 @@
-cmake_minimum_required(VERSION 3.20.0)
-include(CheckCXXCompilerFlag)
-include(CheckIncludeFiles)
-include(GNUInstallDirs)
-
-set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_CXX_STANDARD 17)
-
-project(libbolt_rt_project)
-
-check_include_files(elf.h HAVE_ELF_H)
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
-               ${CMAKE_CURRENT_BINARY_DIR}/config.h)
-
-add_library(bolt_rt_instr STATIC
-  instr.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/config.h
-  )
-set_target_properties(bolt_rt_instr PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}")
-add_library(bolt_rt_hugify STATIC
-  hugify.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/config.h
-  )
-set_target_properties(bolt_rt_hugify PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}")
-
-if(NOT BOLT_BUILT_STANDALONE)
-  add_custom_command(TARGET bolt_rt_instr POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/libbolt_rt_instr.a" "${LLVM_LIBRARY_DIR}")
-  add_custom_command(TARGET bolt_rt_hugify POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/libbolt_rt_hugify.a" "${LLVM_LIBRARY_DIR}")
-endif()
-
-set(BOLT_RT_FLAGS
-  -ffreestanding
-  -fno-exceptions
-  -fno-rtti
-  -fno-stack-protector
-  -fPIC
-  # Runtime currently assumes omitted frame pointers for functions marked __attribute((naked)).
-  # Protect against distros adding -fno-omit-frame-pointer and compiling with GCC.
-  # Refs: llvm/llvm-project#148595 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77882
-  -fomit-frame-pointer
-)
-if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
-  set(BOLT_RT_FLAGS ${BOLT_RT_FLAGS} 
-    -mno-sse 
-    -mgeneral-regs-only)
-endif()
-if (CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64")
-  set(BOLT_RT_FLAGS ${BOLT_RT_FLAGS})
-endif()
-if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
-  check_cxx_compiler_flag("-mno-outline-atomics" CXX_SUPPORTS_OUTLINE_ATOMICS)
-  if (CXX_SUPPORTS_OUTLINE_ATOMICS)
-    set(BOLT_RT_FLAGS ${BOLT_RT_FLAGS} 
-      -mno-outline-atomics 
-      -mgeneral-regs-only)
-  endif()
-endif()
-
-# Don't let the compiler think it can create calls to standard libs
-target_compile_options(bolt_rt_instr PRIVATE ${BOLT_RT_FLAGS})
-target_include_directories(bolt_rt_instr PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_compile_options(bolt_rt_hugify PRIVATE ${BOLT_RT_FLAGS})
-target_include_directories(bolt_rt_hugify PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-
-install(TARGETS bolt_rt_instr DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
-install(TARGETS bolt_rt_hugify DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
-
-if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-  add_library(bolt_rt_instr_osx STATIC
-    instr.cpp
-    ${CMAKE_CURRENT_BINARY_DIR}/config.h
-  )
-  set_target_properties(bolt_rt_instr_osx PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}")
-  target_include_directories(bolt_rt_instr_osx PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-  target_compile_options(bolt_rt_instr_osx PRIVATE
-    -target x86_64-apple-darwin19.6.0
-    ${BOLT_RT_FLAGS})
-  install(TARGETS bolt_rt_instr_osx DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
-
-  if(NOT BOLT_BUILT_STANDALONE)
-    add_custom_command(TARGET bolt_rt_instr_osx POST_BUILD
-      COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/libbolt_rt_instr_osx.a" "${LLVM_LIBRARY_DIR}")
-  endif()
-endif()
diff --git a/bolt/tools/driver/CMakeLists.txt b/bolt/tools/driver/CMakeLists.txt
index 4b3c7416de974..52cbd9d30cd7c 100644
--- a/bolt/tools/driver/CMakeLists.txt
+++ b/bolt/tools/driver/CMakeLists.txt
@@ -5,20 +5,7 @@ set(LLVM_LINK_COMPONENTS
   Support
   )
 
-if (BOLT_ENABLE_RUNTIME)
-  set(BOLT_DRIVER_DEPS "bolt_rt")
-else()
-  set(BOLT_DRIVER_DEPS "")
-endif()
-
-add_bolt_tool(llvm-bolt
-  llvm-bolt.cpp
-
-  DISABLE_LLVM_LINK_LLVM_DYLIB
-
-  DEPENDS
-  ${BOLT_DRIVER_DEPS}
-  )
+add_bolt_tool(llvm-bolt llvm-bolt.cpp DISABLE_LLVM_LINK_LLVM_DYLIB)
 
 target_link_libraries(llvm-bolt
   PRIVATE
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 6001928f92e37..7e211f87a4e2a 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -164,7 +164,7 @@ endforeach()
 # As we migrate runtimes to using the bootstrapping build, the set of default runtimes
 # should grow as we remove those runtimes from LLVM_ENABLE_PROJECTS above.
 set(LLVM_DEFAULT_RUNTIMES "libcxx;libcxxabi;libunwind;libclc;compiler-rt;openmp")
-set(LLVM_SUPPORTED_RUNTIMES "libc;libunwind;libcxxabi;libcxx;compiler-rt;openmp;llvm-libgcc;offload;flang-rt;libclc;libsycl;orc-rt")
+set(LLVM_SUPPORTED_RUNTIMES "bolt-rt;libc;libunwind;libcxxabi;libcxx;compiler-rt;openmp;llvm-libgcc;offload;flang-rt;libclc;libsycl;orc-rt")
 set(LLVM_ENABLE_RUNTIMES "" CACHE STRING
   "Semicolon-separated list of runtimes to build, or \"all\" (${LLVM_DEFAULT_RUNTIMES}). Supported runtimes are ${LLVM_SUPPORTED_RUNTIMES}.")
 if(LLVM_ENABLE_RUNTIMES STREQUAL "all")
@@ -716,6 +716,11 @@ if("flang" IN_LIST LLVM_ENABLE_PROJECTS AND
   list(APPEND LLVM_ENABLE_RUNTIMES "compiler-rt")
 endif()
 
+if("bolt" IN_LIST LLVM_ENABLE_PROJECTS AND NOT ("bolt-rt" IN_LIST LLVM_ENABLE_RUNTIMES))
+  message(STATUS "Enabling bolt-rt as a dependency of bolt.")
+  list(APPEND LLVM_ENABLE_RUNTIMES "bolt-rt")
+endif()
+
 set(LLVM_TARGETS_TO_BUILD
    ${LLVM_TARGETS_TO_BUILD}
    ${LLVM_EXPERIMENTAL_TARGETS_TO_BUILD})
diff --git a/runtimes/CMakeLists.txt b/runtimes/CMakeLists.txt
index eba41b8b10ac4..bd1e31c41bed7 100644
--- a/runtimes/CMakeLists.txt
+++ b/runtimes/CMakeLists.txt
@@ -37,7 +37,7 @@ list(INSERT CMAKE_MODULE_PATH 0
 # compiler-rt can depend on libc++, so we put it after. Offload can depend on
 # flang-rt so we order it before.
 set(LLVM_DEFAULT_RUNTIMES "libc;libunwind;libcxxabi;libcxx;compiler-rt;libclc;openmp;offload")
-set(LLVM_SUPPORTED_RUNTIMES "libc;libunwind;libcxxabi;libcxx;compiler-rt;libclc;openmp;flang-rt;offload;llvm-libgcc;libsycl;orc-rt")
+set(LLVM_SUPPORTED_RUNTIMES "libc;libunwind;libcxxabi;libcxx;compiler-rt;libclc;openmp;flang-rt;offload;llvm-libgcc;libsycl;orc-rt;bolt-rt")
 set(LLVM_ENABLE_RUNTIMES "" CACHE STRING
   "Semicolon-separated list of runtimes to build, or \"all\" (${LLVM_DEFAULT_RUNTIMES}). Supported runtimes are ${LLVM_SUPPORTED_RUNTIMES}.")
 if(LLVM_ENABLE_RUNTIMES STREQUAL "all" )

>From e485b44b4004a3a02f6a3099e58a636253207d0b Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 09:56:18 +0000
Subject: [PATCH 02/13] [BOLT] remove unused cmake code

---
 bolt/CMakeLists.txt | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/bolt/CMakeLists.txt b/bolt/CMakeLists.txt
index 4458c237ebd9f..2bc4ca26e7a4d 100644
--- a/bolt/CMakeLists.txt
+++ b/bolt/CMakeLists.txt
@@ -84,17 +84,6 @@ foreach (tgt ${BOLT_TARGETS_TO_BUILD})
   message(STATUS "Targeting ${tgt} in llvm-bolt")
 endforeach()
 
-set(BOLT_ENABLE_RUNTIME_default OFF)
-if ((CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"
-    OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$"
-    OR CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
-    AND (CMAKE_SYSTEM_NAME STREQUAL "Linux"
-      OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-    AND (NOT CMAKE_CROSSCOMPILING))
-  set(BOLT_ENABLE_RUNTIME_default ON)
-endif()
-option(BOLT_ENABLE_RUNTIME "Enable BOLT runtime" ${BOLT_ENABLE_RUNTIME_default})
-
 set(BOLT_CLANG_EXE "" CACHE FILEPATH "Path to clang executable for the target \
 architecture for use in BOLT tests")
 set(BOLT_LLD_EXE "" CACHE FILEPATH "Path to lld executable for the target \

>From 333a3e30ade754cb24cb140694fc9dcc6f09a6a1 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 09:56:45 +0000
Subject: [PATCH 03/13] [BOLT] Restore missing bolt-rt dependency

---
 bolt/test/CMakeLists.txt         |  6 ++++++
 bolt/tools/driver/CMakeLists.txt | 15 ++++++++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/bolt/test/CMakeLists.txt b/bolt/test/CMakeLists.txt
index 6e18b028bddfc..0f19de43ff2e9 100644
--- a/bolt/test/CMakeLists.txt
+++ b/bolt/test/CMakeLists.txt
@@ -1,3 +1,9 @@
+if("bolt-rt" IN_LIST LLVM_ENABLE_RUNTIMES)
+  set(BOLT_ENABLE_RUNTIME ON)
+else()
+  set(BOLT_ENABLE_RUNTIME OFF)
+endif()
+
 llvm_canonicalize_cmake_booleans(
   BOLT_ENABLE_RUNTIME
   )
diff --git a/bolt/tools/driver/CMakeLists.txt b/bolt/tools/driver/CMakeLists.txt
index 52cbd9d30cd7c..fbb655cb4b3fd 100644
--- a/bolt/tools/driver/CMakeLists.txt
+++ b/bolt/tools/driver/CMakeLists.txt
@@ -5,7 +5,20 @@ set(LLVM_LINK_COMPONENTS
   Support
   )
 
-add_bolt_tool(llvm-bolt llvm-bolt.cpp DISABLE_LLVM_LINK_LLVM_DYLIB)
+if ("bolt-rt" IN_LIST LLVM_ENABLE_RUNTIMES)
+  set(BOLT_DRIVER_DEPS "bolt-rt")
+else()
+  set(BOLT_DRIVER_DEPS "")
+endif()
+
+add_bolt_tool(llvm-bolt
+  llvm-bolt.cpp
+
+  DISABLE_LLVM_LINK_LLVM_DYLIB
+
+  DEPENDS
+  ${BOLT_DRIVER_DEPS}
+  )
 
 target_link_libraries(llvm-bolt
   PRIVATE

>From 38f86611ebdaa1d12289964e5b9bf5e12988b4e7 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 12:23:30 +0000
Subject: [PATCH 04/13] [BOLT] add --target for clang compilers

---
 bolt-rt/CMakeLists.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 2d7c3101d3dd1..22b3d06963348 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -23,6 +23,15 @@ if(NOT BOLT_RT_TARGET_TRIPLE)
   get_host_triple(BOLT_RT_TARGET_TRIPLE)
 endif()
 
+# Append --target for clang compilers. Otherwise it is assumed that
+# one of the following has been configured.
+# 1. CMAKE_CXX_FLAGS for stand alone builds.
+# 2. RUNTIMES_<target-triple>_CMAKE_CXX_FLAGS for runtime builds.
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  list(APPEND BOLT_RT_FLAGS
+       "--target=${BOLT_RT_TARGET_TRIPLE}")
+endif()
+
 message(STATUS "bolt-rt: targeting ${BOLT_RT_TARGET_TRIPLE}")
 
 # Add custom target for runtime builds.

>From 67404911bace88f484d2cc65585fee506be150c7 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 13:23:46 +0000
Subject: [PATCH 05/13] [BOLT] Assume LLVM_ENABLE_PER_TARGET_RUNTIME_DIR

---
 bolt-rt/CMakeLists.txt | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 22b3d06963348..9538fc8d6151a 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -51,14 +51,9 @@ else()
   set(BOLT_RT_LIBRARY_DIR "lib${LLVM_LIBDIR_SUFFIX}")
 endif()
 
-if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR)
-  string(APPEND BOLT_RT_LIBRARY_DIR "/${BOLT_RT_TARGET_TRIPLE}")
-  set(BOLT_RT_ARCHIVE_SUFFIX "")
-else()
-  # TODO(casey.smalley at arm.com): Does this need to account for
-  # LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF?
-  message(FATAL_ERROR "bolt_rt: Not implemented.")
-endif()
+# Act as if LLVM_ENABLE_PER_TARGET_RUNTIME_DIR is always true.
+string(APPEND BOLT_RT_LIBRARY_DIR "/bolt/${BOLT_RT_TARGET_TRIPLE}")
+set(BOLT_RT_ARCHIVE_SUFFIX "")
 
 # Build flags.
 list(APPEND BOLT_RT_FLAGS

>From 2f1c3489eb04c98515d2758b4173cd9117c9f581 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 13:35:56 +0000
Subject: [PATCH 06/13] [BOLT] Fix runtime lookup path

---
 bolt/lib/RuntimeLibs/RuntimeLibrary.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp b/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
index 51f134e22e8e3..0dd9511293b45 100644
--- a/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
+++ b/bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
@@ -42,13 +42,14 @@ std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
   StringRef Dir = llvm::sys::path::parent_path(ToolPath);
   SmallString<128> LibPath = llvm::sys::path::parent_path(Dir);
   llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
-  llvm::sys::path::append(LibPath, opts::TargetTriple);
   if (!llvm::sys::fs::exists(LibPath)) {
     // In some cases we install bolt binary into one level deeper in bin/,
     // we need to go back one more level to find lib directory.
     LibPath = llvm::sys::path::parent_path(llvm::sys::path::parent_path(Dir));
     llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
   }
+  llvm::sys::path::append(LibPath, "bolt");
+  llvm::sys::path::append(LibPath, opts::TargetTriple);
   llvm::sys::path::append(LibPath, LibFileName);
   if (!llvm::sys::fs::exists(LibPath)) {
     // If it is a symlink, check the directory that the symlink points to.
@@ -72,6 +73,7 @@ std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
 
 std::string RuntimeLibrary::getLibPathByInstalled(StringRef LibFileName) {
   SmallString<128> LibPath(CMAKE_INSTALL_FULL_LIBDIR);
+  llvm::sys::path::append(LibPath, "bolt");
   llvm::sys::path::append(LibPath, opts::TargetTriple);
   llvm::sys::path::append(LibPath, LibFileName);
   return std::string(LibPath);

>From d5df9a0a20bb9c8ed3bf63ef52f8fe5127b0e3f2 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 15:33:57 +0000
Subject: [PATCH 07/13] [BOLT] suppress bolt-rt warnings

---
 bolt-rt/CMakeLists.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 9538fc8d6151a..cb6840364337c 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -32,6 +32,15 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        "--target=${BOLT_RT_TARGET_TRIPLE}")
 endif()
 
+# TODO: Fix reported warnings.
+list(APPEND BOLT_RT_FLAGS
+     -Wno-unused-function
+     -Wno-unused-variable
+     -Wno-unused-but-set-variable
+     -Wno-cast-qual
+     -Wno-sign-compare
+     -Wno-unknown-attributes)
+
 message(STATUS "bolt-rt: targeting ${BOLT_RT_TARGET_TRIPLE}")
 
 # Add custom target for runtime builds.

>From 2fced6bbcc7c3c3310a497475bfe6c34c27e5ed2 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 15:52:33 +0000
Subject: [PATCH 08/13] [BOLT] apply clang-format

---
 bolt-rt/common.h  | 12 +++----
 bolt-rt/instr.cpp | 88 +++++++++++++++++++++--------------------------
 2 files changed, 45 insertions(+), 55 deletions(-)

diff --git a/bolt-rt/common.h b/bolt-rt/common.h
index 8689bc8b72041..2ee7c21870c01 100644
--- a/bolt-rt/common.h
+++ b/bolt-rt/common.h
@@ -21,12 +21,12 @@
 
 typedef __SIZE_TYPE__ size_t;
 #define __SSIZE_TYPE__                                                         \
-  __typeof__(_Generic((__SIZE_TYPE__)0, unsigned long long int                 \
-                      : (long long int)0, unsigned long int                    \
-                      : (long int)0, unsigned int                              \
-                      : (int)0, unsigned short                                 \
-                      : (short)0, unsigned char                                \
-                      : (signed char)0))
+  __typeof__(_Generic((__SIZE_TYPE__)0,                                        \
+      unsigned long long int: (long long int)0,                                \
+      unsigned long int: (long int)0,                                          \
+      unsigned int: (int)0,                                                    \
+      unsigned short: (short)0,                                                \
+      unsigned char: (signed char)0))
 typedef __SSIZE_TYPE__ ssize_t;
 
 typedef unsigned long long uint64_t;
diff --git a/bolt-rt/instr.cpp b/bolt-rt/instr.cpp
index 3470e391224ba..a34555b0c96a0 100644
--- a/bolt-rt/instr.cpp
+++ b/bolt-rt/instr.cpp
@@ -43,7 +43,7 @@
 #include "common.h"
 
 // Enables a very verbose logging to stderr useful when debugging
-//#define ENABLE_DEBUG
+// #define ENABLE_DEBUG
 
 #ifdef ENABLE_DEBUG
 #define DEBUG(X)                                                               \
@@ -58,10 +58,10 @@
 extern "C" {
 
 #if defined(__APPLE__)
-extern uint64_t* _bolt_instr_locations_getter();
+extern uint64_t *_bolt_instr_locations_getter();
 extern uint32_t _bolt_num_counters_getter();
 
-extern uint8_t* _bolt_instr_tables_getter();
+extern uint8_t *_bolt_instr_tables_getter();
 extern uint32_t _bolt_instr_num_funcs_getter();
 
 #else
@@ -134,7 +134,8 @@ class BumpPtrAllocator {
 #if defined(__ANDROID__)
   __attribute__((noinline))
 #endif
-  void *allocate(size_t Size) {
+  void *
+  allocate(size_t Size) {
     Lock L(M);
 
     if (StackBase == nullptr) {
@@ -164,7 +165,8 @@ class BumpPtrAllocator {
 #if defined(__ANDROID__)
   __attribute__((noinline))
 #endif
-  void deallocate(void *Ptr) {
+  void
+  deallocate(void *Ptr) {
     Lock L(M);
     uint8_t MetadataOffset = sizeof(EntryMetadata);
     auto *M = reinterpret_cast<EntryMetadata *>(
@@ -243,9 +245,7 @@ void *operator new(size_t Sz, BumpPtrAllocator &A, char C) {
   memset(Ptr, C, Sz);
   return Ptr;
 }
-void *operator new[](size_t Sz, BumpPtrAllocator &A) {
-  return A.allocate(Sz);
-}
+void *operator new[](size_t Sz, BumpPtrAllocator &A) { return A.allocate(Sz); }
 void *operator new[](size_t Sz, BumpPtrAllocator &A, char C) {
   auto *Ptr = reinterpret_cast<char *>(A.allocate(Sz));
   memset(Ptr, C, Sz);
@@ -414,8 +414,8 @@ class SimpleHashTable {
     // Defer to the next level
     if (Entry.Key & FollowUpTableMarker) {
       return getEntry(
-          reinterpret_cast<MapEntry *>(Entry.Key & ~FollowUpTableMarker),
-          Key, Remainder, Alloc, CurLevel + 1);
+          reinterpret_cast<MapEntry *>(Entry.Key & ~FollowUpTableMarker), Key,
+          Remainder, Alloc, CurLevel + 1);
     }
 
     // Conflict - create the next level
@@ -449,9 +449,7 @@ class SimpleHashTable {
   }
 };
 
-template <typename T> void resetIndCallCounter(T &Entry) {
-  Entry.Val = 0;
-}
+template <typename T> void resetIndCallCounter(T &Entry) { Entry.Val = 0; }
 
 template <typename T, uint32_t X, uint32_t Y>
 void SimpleHashTable<T, X, Y>::resetCounters() {
@@ -582,10 +580,10 @@ struct ProfileWriterContext {
   const IndCallTargetDescription *IndCallTargets;
   const uint8_t *FuncDescriptions;
   const char *Strings; // String table with function names used in this binary
-  int FileDesc;   // File descriptor for the file on disk backing this
-                  // information in memory via mmap
+  int FileDesc;        // File descriptor for the file on disk backing this
+                       // information in memory via mmap
   const void *MMapPtr; // The mmap ptr
-  int MMapSize;   // The mmap size
+  int MMapSize;        // The mmap size
 
   /// Hash table storing all possible call destinations to detect untracked
   /// calls and correctly report them as [unknown] in output fdata.
@@ -612,8 +610,9 @@ int compareStr(const char *Str1, const char *Str2, int Size) {
 #if defined(__ANDROID__)
 __attribute__((noinline))
 #endif
-char *serializeLoc(const ProfileWriterContext &Ctx, char *OutBuf,
-                   const Location Loc, uint32_t BufSize) {
+char *
+serializeLoc(const ProfileWriterContext &Ctx, char *OutBuf, const Location Loc,
+             uint32_t BufSize) {
   // fdata location format: Type Name Offset
   // Type 1 - regular symbol
   OutBuf = strCopy(OutBuf, "1 ");
@@ -860,7 +859,6 @@ void printStats(const ProfileWriterContext &Ctx) {
 }
 #endif
 
-
 /// This is part of a simple CFG representation in memory, where we store
 /// a dynamically sized array of input and output edges per node, and store
 /// a dynamically sized array of nodes per graph. We also store the spanning
@@ -948,8 +946,8 @@ Graph::Graph(BumpPtrAllocator &Alloc, const FunctionDescription &D,
 
   DEBUG(reportNumber("G->CFGNodes = 0x", (uint64_t)CFGNodes, 16));
   SpanningTreeNodes = new (Alloc) Node[MaxNodes];
-  DEBUG(reportNumber("G->SpanningTreeNodes = 0x",
-                     (uint64_t)SpanningTreeNodes, 16));
+  DEBUG(reportNumber("G->SpanningTreeNodes = 0x", (uint64_t)SpanningTreeNodes,
+                     16));
 
   // Figure out how much to allocate to each vector (in/out edge sets)
   for (int I = 0; I < D.NumEdges; ++I) {
@@ -995,13 +993,11 @@ Graph::Graph(BumpPtrAllocator &Alloc, const FunctionDescription &D,
     if (D.Edges[I].Counter != 0xffffffff)
       continue;
 
-    E = &SpanningTreeNodes[Src]
-             .OutEdges[SpanningTreeNodes[Src].NumOutEdges++];
+    E = &SpanningTreeNodes[Src].OutEdges[SpanningTreeNodes[Src].NumOutEdges++];
     E->Node = Dst;
     E->ID = I;
 
-    E = &SpanningTreeNodes[Dst]
-             .InEdges[SpanningTreeNodes[Dst].NumInEdges++];
+    E = &SpanningTreeNodes[Dst].InEdges[SpanningTreeNodes[Dst].NumInEdges++];
     E->Node = Src;
     E->ID = I;
   }
@@ -1148,8 +1144,8 @@ void Graph::computeEdgeFrequencies(const uint64_t *Counters,
   if (NumNodes == 0)
     return;
 
-  EdgeFreqs = D.NumEdges ? new (Alloc, 0) uint64_t [D.NumEdges] : nullptr;
-  CallFreqs = D.NumCalls ? new (Alloc, 0) uint64_t [D.NumCalls] : nullptr;
+  EdgeFreqs = D.NumEdges ? new (Alloc, 0) uint64_t[D.NumEdges] : nullptr;
+  CallFreqs = D.NumCalls ? new (Alloc, 0) uint64_t[D.NumCalls] : nullptr;
 
   // Setup a lookup for calls present in each node (BB)
   NodeToCallsMap *CallMap = new (Alloc) NodeToCallsMap(Alloc, D, NumNodes);
@@ -1158,7 +1154,7 @@ void Graph::computeEdgeFrequencies(const uint64_t *Counters,
   // spanning tree don't have explicit counters. We must infer their value using
   // a linear combination of other counters (sum of counters of the outgoing
   // edges minus sum of counters of the incoming edges).
-  uint32_t *Stack = new (Alloc) uint32_t [NumNodes];
+  uint32_t *Stack = new (Alloc) uint32_t[NumNodes];
   uint32_t StackTop = 0;
   enum Status : uint8_t { S_NEW = 0, S_VISITING, S_VISITED };
   Status *Visited = new (Alloc, 0) Status[NumNodes];
@@ -1179,8 +1175,8 @@ void Graph::computeEdgeFrequencies(const uint64_t *Counters,
   for (int I = 0; I < D.NumEntryNodes; ++I) {
     EntryAddress[D.EntryNodes[I].Node] = D.EntryNodes[I].Address;
     DEBUG({
-        reportNumber("Entry Node# ", D.EntryNodes[I].Node, 10);
-        reportNumber("      Address: ", D.EntryNodes[I].Address, 16);
+      reportNumber("Entry Node# ", D.EntryNodes[I].Node, 10);
+      reportNumber("      Address: ", D.EntryNodes[I].Address, 16);
     });
   }
   // Add all root nodes to the stack
@@ -1413,9 +1409,8 @@ ProfileWriterContext::lookupIndCallTarget(uint64_t Target) const {
 }
 
 /// Write a single indirect call <src, target> pair to the fdata file
-void visitIndCallCounter(IndirectCallHashTable::MapEntry &Entry,
-                         int FD, int CallsiteID,
-                         ProfileWriterContext *Ctx) {
+void visitIndCallCounter(IndirectCallHashTable::MapEntry &Entry, int FD,
+                         int CallsiteID, ProfileWriterContext *Ctx) {
   if (Entry.Val == 0)
     return;
   DEBUG(reportNumber("Target func 0x", Entry.Key, 16));
@@ -1506,8 +1501,8 @@ int openProfile() {
   if (static_cast<int64_t>(FD) < 0) {
     report("Error while trying to open profile file for writing: ");
     report(Buf);
-    reportNumber("\nFailed with error number: 0x",
-                 0 - static_cast<int64_t>(FD), 16);
+    reportNumber("\nFailed with error number: 0x", 0 - static_cast<int64_t>(FD),
+                 16);
     __exit(1);
   }
   return FD;
@@ -1713,8 +1708,7 @@ instrumentIndirectCall(uint64_t Target, uint64_t IndCallID) {
 
 /// We receive as in-stack arguments the identifier of the indirect call site
 /// as well as the target address for the call
-extern "C" __attribute((naked)) void __bolt_instr_indirect_call()
-{
+extern "C" __attribute((naked)) void __bolt_instr_indirect_call() {
 #if defined(__aarch64__)
   // clang-format off
   __asm__ __volatile__(SAVE_ALL
@@ -1748,8 +1742,7 @@ extern "C" __attribute((naked)) void __bolt_instr_indirect_call()
 #endif
 }
 
-extern "C" __attribute((naked)) void __bolt_instr_indirect_tailcall()
-{
+extern "C" __attribute((naked)) void __bolt_instr_indirect_tailcall() {
 #if defined(__aarch64__)
   // clang-format off
   __asm__ __volatile__(SAVE_ALL
@@ -1783,8 +1776,7 @@ extern "C" __attribute((naked)) void __bolt_instr_indirect_tailcall()
 }
 
 /// This is hooking ELF's entry, it needs to save all machine state.
-extern "C" __attribute((naked)) void __bolt_instr_start()
-{
+extern "C" __attribute((naked)) void __bolt_instr_start() {
 #if defined(__aarch64__)
   // clang-format off
   __asm__ __volatile__(SAVE_ALL
@@ -1872,10 +1864,9 @@ extern "C" void __bolt_instr_data_dump() {
 
 // On OSX/iOS the final symbol name of an extern "C" function/variable contains
 // one extra leading underscore: _bolt_instr_setup -> __bolt_instr_setup.
-extern "C"
-__attribute__((section("__TEXT,__setup")))
-__attribute__((force_align_arg_pointer))
-void _bolt_instr_setup() {
+extern "C" __attribute__((section("__TEXT,__setup")))
+__attribute__((force_align_arg_pointer)) void
+_bolt_instr_setup() {
   __asm__ __volatile__(SAVE_ALL :::);
 
   report("Hello!\n");
@@ -1883,10 +1874,9 @@ void _bolt_instr_setup() {
   __asm__ __volatile__(RESTORE_ALL :::);
 }
 
-extern "C"
-__attribute__((section("__TEXT,__fini")))
-__attribute__((force_align_arg_pointer))
-void _bolt_instr_fini() {
+extern "C" __attribute__((section("__TEXT,__fini")))
+__attribute__((force_align_arg_pointer)) void
+_bolt_instr_fini() {
   report("Bye!\n");
   __bolt_instr_data_dump();
 }

>From b3bc9c3114f8b4fa37ab5f30ff1c25be0779b88b Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Mon, 23 Mar 2026 16:45:12 +0000
Subject: [PATCH 09/13] [BOLT] fix install paths

---
 bolt-rt/CMakeLists.txt | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index cb6840364337c..870569b72a1c4 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -49,7 +49,7 @@ if(LLVM_RUNTIMES_BUILD)
   set_target_properties(bolt-rt PROPERTIES FOLDER "BOLT-RT/Metatargets")
   add_llvm_install_targets(install-bolt-rt
                            DEPENDS bolt-rt
-                           COMPONENTS bolt-rt)
+                           COMPONENT bolt-rt)
 endif()
 
 # For runtime builds, the installation directory is already known,
@@ -61,8 +61,7 @@ else()
 endif()
 
 # Act as if LLVM_ENABLE_PER_TARGET_RUNTIME_DIR is always true.
-string(APPEND BOLT_RT_LIBRARY_DIR "/bolt/${BOLT_RT_TARGET_TRIPLE}")
-set(BOLT_RT_ARCHIVE_SUFFIX "")
+set(BOLT_RT_LIBRARY_SUBDIR "/bolt/${BOLT_RT_TARGET_TRIPLE}")
 
 # Build flags.
 list(APPEND BOLT_RT_FLAGS
@@ -108,8 +107,8 @@ function(add_bolt_rt_library)
 
   # Ouput archives into a per-triple path or with a per triple suffix.
   set_target_properties(${ARG_TARGET} PROPERTIES
-                        ARCHIVE_OUTPUT_DIRECTORY "${BOLT_RT_LIBRARY_DIR}"
-                        ARCHIVE_OUTPUT_NAME "${ARG_TARGET}${BOLT_RT_ARCHIVE_SUFFIX}")
+                        ARCHIVE_OUTPUT_DIRECTORY "${BOLT_RT_LIBRARY_DIR}/${BOLT_RT_LIBRARY_SUBDIR}"
+                        ARCHIVE_OUTPUT_NAME "${ARG_TARGET}")
 
   if(LLVM_RUNTIMES_BUILD)
     add_dependencies(bolt-rt ${ARG_TARGET})
@@ -117,7 +116,15 @@ function(add_bolt_rt_library)
 
   # Install to a matching directory.
   install(TARGETS ${ARG_TARGET}
-          DESTINATION "${BOLT_RT_LIBRARY_DIR}")
+          ARCHIVE
+          DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BOLT_RT_LIBRARY_SUBDIR}"
+          COMPONENT bolt-rt)
+
+  if(NOT LLVM_ENABLE_IDE)
+    add_llvm_install_targets(install-${ARG_TARGET}
+                             DEPENDS ${ARG_TARGET}
+                             COMPONENT bolt-rt)
+  endif()
 endfunction()
 
 add_bolt_rt_library(TARGET bolt_rt_instr

>From 51968e03ed7ad80a0204803851ac240329859550 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Wed, 15 Apr 2026 14:40:34 +0100
Subject: [PATCH 10/13] [BOLT] update docs

---
 bolt-rt/CMakeLists.txt      | 2 +-
 bolt/docs/RuntimeLibrary.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 870569b72a1c4..0ebb2a9dcf3f4 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -105,7 +105,7 @@ function(add_bolt_rt_library)
   target_compile_options(${ARG_TARGET}
                          PRIVATE ${BOLT_RT_FLAGS} ${ARG_FLAGS})
 
-  # Ouput archives into a per-triple path or with a per triple suffix.
+  # Ouput archives into a per-triple path.
   set_target_properties(${ARG_TARGET} PROPERTIES
                         ARCHIVE_OUTPUT_DIRECTORY "${BOLT_RT_LIBRARY_DIR}/${BOLT_RT_LIBRARY_SUBDIR}"
                         ARCHIVE_OUTPUT_NAME "${ARG_TARGET}")
diff --git a/bolt/docs/RuntimeLibrary.md b/bolt/docs/RuntimeLibrary.md
index b969ebd3e3547..b5f6c65120e02 100644
--- a/bolt/docs/RuntimeLibrary.md
+++ b/bolt/docs/RuntimeLibrary.md
@@ -15,7 +15,7 @@ However, this approach quickly becomes awkward if we want to insert a lot of cod
 Currently, our runtime library is written in C++ and contains code that helps us instrument a binary.
 
 ### Limitations
-Our library is not written with regular C++ code as it is not linked against any other libraries (this means we cannot rely on anything defined on libstdc++, glibc, libgcc etc), but is self sufficient. In runtime/CMakeLists.txt, we can see it is built with -ffreestanding, which requires the compiler to avoid using a runtime library by itself.
+Our library is not written with regular C++ code as it is not linked against any other libraries (this means we cannot rely on anything defined on libstdc++, glibc, libgcc etc), but is self sufficient. In bolt-rt/CMakeLists.txt, we can see it is built with -ffreestanding, which requires the compiler to avoid using a runtime library by itself.
 
 While this requires us to make our own syscalls, it does simplify our linker a lot, which is very limited and can only do basic function name resolving. However, this is a big improvement in comparison with programmatically generating the code in assembly language using MCInsts.
 

>From aae2113d3b24425d77a0fd64a00c4ed9ddbad48a Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Wed, 15 Apr 2026 14:40:46 +0100
Subject: [PATCH 11/13] [BOLT] fix section-orders.test

---
 bolt-rt/CMakeLists.txt | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 0ebb2a9dcf3f4..9390bad2f0f6a 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -32,14 +32,20 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
        "--target=${BOLT_RT_TARGET_TRIPLE}")
 endif()
 
-# TODO: Fix reported warnings.
 list(APPEND BOLT_RT_FLAGS
+     # TODO: Fix reported warnings.
      -Wno-unused-function
      -Wno-unused-variable
      -Wno-unused-but-set-variable
      -Wno-cast-qual
      -Wno-sign-compare
-     -Wno-unknown-attributes)
+     -Wno-unknown-attributes
+     # Disable function/data sections, these are set in
+     # llvm/cmake/modules/HandleLLVMOptions.cmake.
+     # Leaving them turned on breaks
+     # bolt/test/runtimes/X86/section-order.test.
+     -fno-function-sections
+     -fno-data-sections)
 
 message(STATUS "bolt-rt: targeting ${BOLT_RT_TARGET_TRIPLE}")
 
@@ -102,6 +108,9 @@ function(add_bolt_rt_library)
   target_include_directories(${ARG_TARGET}
                              PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
 
+  # Use C++17
+  target_compile_features(${ARG_TARGET} PUBLIC cxx_std_17)
+
   target_compile_options(${ARG_TARGET}
                          PRIVATE ${BOLT_RT_FLAGS} ${ARG_FLAGS})
 

>From b7f94e11e2cf452b39e8341583dbcbe015d25817 Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Thu, 16 Apr 2026 13:40:35 +0100
Subject: [PATCH 12/13] [BOLT] add check for standalone installations

---
 bolt-rt/CMakeLists.txt | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/bolt-rt/CMakeLists.txt b/bolt-rt/CMakeLists.txt
index 9390bad2f0f6a..676a9470d1f0c 100644
--- a/bolt-rt/CMakeLists.txt
+++ b/bolt-rt/CMakeLists.txt
@@ -1,10 +1,5 @@
 cmake_minimum_required(VERSION 3.20.0)
 
-include(CheckCXXCompilerFlag)
-include(CheckIncludeFiles)
-include(CMakeParseArguments)
-include(GNUInstallDirs)
-
 # Is this built via -DLLVM_ENABLE_RUNTIMES="bolt-rt"?.
 if(LLVM_RUNTIMES_BUILD)
   set(LLVM_SUBPROJECT_TITLE "Bolt-RT")
@@ -13,6 +8,11 @@ elseif(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
   project(bolt-rt)
 endif()
 
+include(CheckCXXCompilerFlag)
+include(CheckIncludeFiles)
+include(CMakeParseArguments)
+include(GNUInstallDirs)
+
 # Use LLVM_RUNTIMES_TARGET when built under runtimes.
 if(LLVM_RUNTIMES_BUILD)
   set(BOLT_RT_TARGET_TRIPLE "${LLVM_RUNTIMES_TARGET}")
@@ -129,7 +129,7 @@ function(add_bolt_rt_library)
           DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BOLT_RT_LIBRARY_SUBDIR}"
           COMPONENT bolt-rt)
 
-  if(NOT LLVM_ENABLE_IDE)
+  if(LLVM_RUNTIMES_BUILD AND NOT LLVM_ENABLE_IDE)
     add_llvm_install_targets(install-${ARG_TARGET}
                              DEPENDS ${ARG_TARGET}
                              COMPONENT bolt-rt)

>From 7db6e433259fc00df622499ee6fb765a7198cb7d Mon Sep 17 00:00:00 2001
From: Casey Smalley <casey.smalley at arm.com>
Date: Thu, 16 Apr 2026 13:40:43 +0100
Subject: [PATCH 13/13] [BOLT] update README

---
 bolt/README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/bolt/README.md b/bolt/README.md
index 40bd483a9125f..73252b2d0ff0a 100644
--- a/bolt/README.md
+++ b/bolt/README.md
@@ -175,6 +175,61 @@ instrumenting the binary with BOLT and then running it.
 llvm-bolt <executable> -instrument -o <instrumented-executable>
 ```
 
+Instrumentation relies on a runtime library which is compiled as part of
+llvm runtimes, similar to compiler-rt. Thus it can be cross compiled for
+multiple targets as a part of an LLVM build. This enables bolt to
+instrument binaries across architectures.
+
+Cross compiling bolt-rtj
+```
+> ANDROID_NDK_BIN="<android ndk>/toolchains/llvm/prebuilt/linux-x86_64/bin"
+> cmake \
+    -S llvm-project/llvm \
+    -B build \
+    -G Ninja \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DLLVM_ENABLE_PROJECTS="clang;lld;bolt" \
+    -DLLVM_ENABLE_RUNTIMES="bolt-rt" \
+    -DLLVM_TARGETS_TO_BUILD="X86;AArch64" \
+    -DLLVM_RUNTIME_TARGETS="x86_64-unknown-linux-gnu;aarch64-unknown-linux-android21" \
+    -DRUNTIMES_x86_64-unknown-linux-gnu_CMAKE_CXX_COMPILER=clang++ \
+    -DRUNTIMES_aarch64-unknown-linux-android21_CMAKE_CXX_COMPILER=$ANDROID_NDK_BIN/clang++
+
+> cmake --build build --target bolt-rt
+```
+
+Or bolt-rt can be cross compiled as a stand alone build.
+```
+> ANDROID_NDK_BIN="<android ndk>/toolchains/llvm/prebuilt/linux-x86_64/bin"
+> cmake \
+    -S llvm-project/bolt-rt\
+    -B build \
+    -G Ninja \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DCMAKE_CXX_COMPILER=$ANDROID_NDK_BIN/clang++ \
+    -DBOLT_RT_TARGET_TRIPLE=aarch64-unknown-linux-android21
+
+> cmake --build build --target libbolt_rt_instr.a
+```
+
+Then a binary can be cross instrumented by specifying a target.
+```
+llvm-bolt \
+  <executable> \
+  -instrument \
+  -target aarch64-unknown-linux-gnu \
+  -o <instrumented executable> 
+```
+
+Or with a direct path to the compiled runtime library.
+```
+llvm-bolt \
+  <executable> \
+  -instrument \
+  -runtime-instrumentation-lib <llvm build>/lib/bolt/aarch64-unknown-linux-android21/libbolt_rt_instr.a \
+  -o <instrumented executable> 
+```
+
 After you run instrumented-executable with the desired workload, its BOLT
 profile should be ready for you in `/tmp/prof.fdata` and you can skip
 **Step 2**.



More information about the llvm-commits mailing list