From 3930fb13a5ff5972bfcbfd5d8c004203c5cfe3fb Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Tue, 18 Nov 2025 00:23:29 -0800 Subject: [PATCH] Begin building libunwind.a as part of the runtimes (#6381) This is the first real step towards building libc++ itself, and fleshes out both the core runtimes management logic and the archive-based runtimes logic for a quite simple runtime. Nothing here causes us to _use_ libunwind, and in fact this doesn't include even the "on-demand" aspect of building `libunwind`. Instead, this just wires it up to the explicit `build-runtimes` subcommand for simple testing. The full integration along side the target directory is future work. --- MODULE.bazel | 1 + ...asic_sources_exporting_for_libunwind.patch | 34 +++++ toolchain/base/runtime_sources.bzl | 18 ++- toolchain/driver/BUILD | 1 + .../driver/build_runtimes_subcommand.cpp | 4 + toolchain/driver/clang_runtimes.cpp | 121 ++++++++++++++++++ toolchain/driver/clang_runtimes.h | 54 ++++++++ toolchain/driver/runtimes_cache.h | 3 + toolchain/install/BUILD | 9 ++ toolchain/install/install_paths.cpp | 5 + toolchain/install/install_paths.h | 3 + 11 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch diff --git a/MODULE.bazel b/MODULE.bazel index 94bb4864acc3..25361faf52ee 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -116,6 +116,7 @@ http_archive( "@carbon//bazel/llvm_project:0001_Patch_for_mallinfo2_when_using_Bazel_build_system.patch", "@carbon//bazel/llvm_project:0002_Added_Bazel_build_for_compiler_rt_fuzzer.patch", "@carbon//bazel/llvm_project:0003_Comment_out_unloaded_proto_library_dependencies.patch", + "@carbon//bazel/llvm_project:0004_Introduce_basic_sources_exporting_for_libunwind.patch", ], strip_prefix = "llvm-project-{0}".format(llvm_project_version), urls = ["https://github.com/llvm/llvm-project/archive/{0}.tar.gz".format(llvm_project_version)], diff --git a/bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch b/bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch new file mode 100644 index 000000000000..715283962b62 --- /dev/null +++ b/bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch @@ -0,0 +1,34 @@ +Commit ID: a79f1facf56cc5772557cdb09d811aa11a22e43b +Change ID: sxspxmonsuvqzuvxvrvorlumwpwromsv +Author : Chandler Carruth (2025-09-25 22:55:26) +Committer: Chandler Carruth (2025-11-17 09:59:25) + + Introduce basic sources exporting for libunwind + + This exports the source files directly so that they can be used to build + this runtime library on demand. + +diff --git a/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel b/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel +index c9fdc819c0..7d734c5a06 100644 +--- a/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel ++++ b/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel +@@ -21,3 +21,19 @@ + ], + strip_include_prefix = "include", + ) ++ ++filegroup( ++ name = "libunwind_hdrs", ++ srcs = glob(["include/**/*.h"]), ++) ++ ++filegroup( ++ name = "libunwind_srcs", ++ srcs = glob([ ++ "src/*.cpp", ++ "src/*.hpp", ++ "src/*.c", ++ "src/*.h", ++ "src/*.S", ++ ]), ++) diff --git a/toolchain/base/runtime_sources.bzl b/toolchain/base/runtime_sources.bzl index 4212cfc5044a..1fce76f345f8 100644 --- a/toolchain/base/runtime_sources.bzl +++ b/toolchain/base/runtime_sources.bzl @@ -37,6 +37,10 @@ BUILTINS_FILEGROUPS = { "x86_fp80_srcs": "@llvm-project//compiler-rt:builtins_x86_fp80_srcs", } +RUNTIMES_FILEGROUPS = { + "libunwind_srcs": "@llvm-project//libunwind:libunwind_srcs", +} + _TEMPLATE = """ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. @@ -85,6 +89,10 @@ inline constexpr llvm::StringLiteral BuiltinsI386Srcs[] = {{ {i386_srcs} }}; +constexpr inline llvm::StringLiteral LibunwindSrcs[] = {{ +{libunwind_srcs} +}}; + }} // namespace Carbon::RuntimeSources #endif // CARBON_TOOLCHAIN_BASE_RUNTIME_SOURCES_H_ @@ -99,6 +107,10 @@ def _builtins_path(file): # label name. return file.owner.name.removeprefix("lib/") +def _runtimes_path(file): + """Returns the runtime install path for a file in a normal runtimes library.""" + return file.owner.name + def _get_path(file_attr, to_path_fn): files = file_attr[DefaultInfo].files.to_list() if len(files) > 1: @@ -125,6 +137,9 @@ def _generate_runtime_sources_h_rule(ctx): } | { k: _get_paths(getattr(ctx.attr, "_" + k), _builtins_path) for k in BUILTINS_FILEGROUPS.keys() + } | { + k: _get_paths(getattr(ctx.attr, "_" + k), _runtimes_path) + for k in RUNTIMES_FILEGROUPS.keys() }))) return [DefaultInfo(files = depset([h_file]))] @@ -135,7 +150,8 @@ generate_runtime_sources_h = rule( for k, v in CRT_FILES.items() } | { "_" + k: attr.label_list(default = [v], allow_files = True) - for k, v in BUILTINS_FILEGROUPS.items() + for k, v in BUILTINS_FILEGROUPS.items() + RUNTIMES_FILEGROUPS.items() + } | { }, ) diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 0d10a50d4bee..067d7d7d9e3c 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -37,6 +37,7 @@ cc_library( "//common:latch", "//common:ostream", "//common:vlog", + "//toolchain/base:kind_switch", "//toolchain/base:runtime_sources", "//toolchain/install:install_paths", "@llvm-project//clang:basic", diff --git a/toolchain/driver/build_runtimes_subcommand.cpp b/toolchain/driver/build_runtimes_subcommand.cpp index af3dd2d33b6d..e6a9dc514604 100644 --- a/toolchain/driver/build_runtimes_subcommand.cpp +++ b/toolchain/driver/build_runtimes_subcommand.cpp @@ -94,8 +94,12 @@ auto BuildRuntimesSubcommand::RunInternal(DriverEnv& driver_env) ClangResourceDirBuilder resource_dir_builder(&runner, driver_env.thread_pool, llvm::Triple(features.target), &runtimes); + ClangArchiveRuntimesBuilder lib_unwind_builder( + &runner, driver_env.thread_pool, llvm::Triple(features.target), + &runtimes); CARBON_RETURN_IF_ERROR(std::move(resource_dir_builder).Wait()); + CARBON_RETURN_IF_ERROR(std::move(lib_unwind_builder).Wait()); return runtimes.base_path(); } diff --git a/toolchain/driver/clang_runtimes.cpp b/toolchain/driver/clang_runtimes.cpp index 06ab5570ab90..369ca3b93cee 100644 --- a/toolchain/driver/clang_runtimes.cpp +++ b/toolchain/driver/clang_runtimes.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Triple.h" +#include "toolchain/base/kind_switch.h" #include "toolchain/base/runtime_sources.h" #include "toolchain/driver/clang_runner.h" #include "toolchain/driver/runtimes_cache.h" @@ -210,6 +211,126 @@ auto ClangRuntimesBuilderBase::ArchiveBuilder::CompileMember( return std::move(*obj_result); } +template + requires(Component == Runtimes::LibUnwind) +ClangArchiveRuntimesBuilder::ClangArchiveRuntimesBuilder( + ClangRunner* clang, llvm::ThreadPoolInterface* threads, + llvm::Triple target_triple, Runtimes* runtimes) + : ClangRuntimesBuilderBase(clang, threads, std::move(target_triple)) { + // Ensure we're on a platform where we _can_ build a working runtime. + if (target_triple_.isOSWindows()) { + result_ = + Error("TODO: Windows runtimes are untested and not yet supported."); + return; + } + + auto build_dir_or_error = runtimes->Build(Component); + if (!build_dir_or_error.ok()) { + result_ = std::move(build_dir_or_error).error(); + return; + } + auto build_dir = *(std::move(build_dir_or_error)); + CARBON_KIND_SWITCH(std::move(build_dir)) { + case CARBON_KIND(std::filesystem::path build_dir_path): { + // Found cached build. + result_ = std::move(build_dir_path); + return; + } + case CARBON_KIND(Runtimes::Builder builder): { + runtimes_builder_ = std::move(builder); + // Building the runtimes is handled below. + break; + } + } + + if constexpr (Component == Runtimes::LibUnwind) { + srcs_path_ = installation().libunwind_path(); + include_path_ = installation().libunwind_path() / "include"; + archive_path_ = std::filesystem::path("lib") / "libunwind.a"; + } else { + static_assert(false, + "Invalid runtimes component for an archive runtime builder."); + } + + archive_.emplace(this, archive_path_, srcs_path_, CollectSrcFiles(), + CollectCflags()); + tasks_.async([this]() mutable { Setup(); }); +} + +template + requires(Component == Runtimes::LibUnwind) +auto ClangArchiveRuntimesBuilder::CollectSrcFiles() + -> llvm::SmallVector { + if constexpr (Component == Runtimes::LibUnwind) { + return llvm::SmallVector(llvm::make_filter_range( + RuntimeSources::LibunwindSrcs, [](llvm::StringRef src) { + return src.ends_with(".c") || src.ends_with(".cpp") || + src.ends_with(".S"); + })); + } else { + static_assert(false, + "Invalid runtimes component for an archive runtime builder."); + } +} + +template + requires(Component == Runtimes::LibUnwind) +auto ClangArchiveRuntimesBuilder::CollectCflags() + -> llvm::SmallVector { + if constexpr (Component == Runtimes::LibUnwind) { + return { + "-no-canonical-prefixes", + "-O3", + "-fPIC", + "-funwind-tables", + "-fno-exceptions", + "-fno-rtti", + "-nostdinc++", + "-I", + include_path_.native(), + "-D_LIBUNWIND_IS_NATIVE_ONLY", + "-w", + }; + } else { + static_assert(false, + "Invalid runtimes component for an archive runtime builder."); + } +} + +template + requires(Component == Runtimes::LibUnwind) +auto ClangArchiveRuntimesBuilder::Setup() -> void { + // Symlink the installation's `include` into the runtime. + CARBON_CHECK(include_path_.is_absolute(), + "Unexpected relative include path: {0}", include_path_); + if (auto result = runtimes_builder_->dir().Symlink("include", include_path_); + !result.ok()) { + result_ = std::move(result).error(); + return; + } + + // Finish building the runtime once the archive is built. + Latch::Handle latch_handle = step_counter_.Init( + [this]() mutable { tasks_.async([this]() mutable { Finish(); }); }); + + // Start building the archive itself with a handle to detect when complete. + archive_->Setup(std::move(latch_handle)); +} + +template + requires(Component == Runtimes::LibUnwind) +auto ClangArchiveRuntimesBuilder::Finish() -> void { + CARBON_VLOG("Finished building {0}...\n", archive_path_); + if (!archive_->result().ok()) { + result_ = std::move(archive_->result()).error(); + return; + } + + result_ = (*std::move(runtimes_builder_)).Commit(); +} + +template class ClangArchiveRuntimesBuilder; + ClangResourceDirBuilder::ClangResourceDirBuilder( ClangRunner* clang, llvm::ThreadPoolInterface* threads, llvm::Triple target_triple, Runtimes* runtimes) diff --git a/toolchain/driver/clang_runtimes.h b/toolchain/driver/clang_runtimes.h index b2aa3272015f..cf7d26c3259e 100644 --- a/toolchain/driver/clang_runtimes.h +++ b/toolchain/driver/clang_runtimes.h @@ -203,6 +203,60 @@ class ClangRuntimesBuilderBase::ArchiveBuilder { ErrorOr result_ = Error("No archive built!"); }; +// A class template to build runtimes consisting of a single archive. +// +// The template argument comes from the `Runtimes::Component` enum, but is only +// intended for Clang-runtimes that consist of a single archive. We use a +// requires to enforce that the components used are exactly one of those +// supported so we can also move instantiation into the `.cpp` file. +template + requires(Component == Runtimes::LibUnwind) +class ClangArchiveRuntimesBuilder : public ClangRuntimesBuilderBase { + public: + // Constructing this class will attempt to build the `Component` archive into + // `runtimes`. + // + // If an existing build is found, it will immediately be available. + // Otherwise, constructing this class will schedule asynchronous work on + // `threads` to build the archive on-demand using `clang`. + // + // Once constructed, callers may call `Wait` (from the base class) to wait + // until the asynchronous work is complete and the runtimes are available. If + // they were already available, the call to `Wait` will not block. + ClangArchiveRuntimesBuilder(ClangRunner* clang, + llvm::ThreadPoolInterface* threads, + llvm::Triple target_triple, Runtimes* runtimes); + + private: + // Helpers to compute the list of source files and compile flags for a + // particular archive. The implementations of these are expected to be + // specialized for each different `Component`. + auto CollectSrcFiles() -> llvm::SmallVector; + auto CollectCflags() -> llvm::SmallVector; + + // Helper to encapsulate the initial, but still asynchronous setup work. + auto Setup() -> void; + + // Helper to encapsulate the final asynchronous step in building the resource + // directory. + auto Finish() -> void; + + // The root path used for any of the source files. + std::filesystem::path srcs_path_; + + // The (absolute) include path used during the compilation of the source + // files. + std::filesystem::path include_path_; + + // The relative archive path within the runtimes' build directory. + std::filesystem::path archive_path_; + + // The archive builder if it is necessary to build the archive. + std::optional archive_; +}; + +extern template class ClangArchiveRuntimesBuilder; + // Builds the target-specific resource directory for Clang. // // There is a resource directory installed along side the Clang binary that diff --git a/toolchain/driver/runtimes_cache.h b/toolchain/driver/runtimes_cache.h index c8d06c2ae946..e499af1eaf85 100644 --- a/toolchain/driver/runtimes_cache.h +++ b/toolchain/driver/runtimes_cache.h @@ -46,6 +46,7 @@ class Runtimes { enum Component { ClangResourceDir, + LibUnwind, NumComponents, }; @@ -136,6 +137,8 @@ class Runtimes { switch (component) { case ClangResourceDir: return "clang_resource_dir"; + case LibUnwind: + return "libunwind"; case NumComponents: CARBON_FATAL("Invalid component"); } diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index ec2dae8e1373..acb92291faf0 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -162,6 +162,14 @@ filegroup( srcs = CRT_FILES.values() + BUILTINS_FILEGROUPS.values(), ) +filegroup( + name = "libunwind", + srcs = [ + "@llvm-project//libunwind:libunwind_hdrs", + "@llvm-project//libunwind:libunwind_srcs", + ], +) + # Given a root `prefix_root`, the hierarchy looks like: # # - prefix_root/bin: Binaries intended for direct use. @@ -195,6 +203,7 @@ install_dirs = { is_driver = True, ), install_filegroup("core", "//core:prelude"), + install_filegroup("libunwind", ":libunwind"), ], "lib/carbon/llvm/bin": [install_symlink( name, diff --git a/toolchain/install/install_paths.cpp b/toolchain/install/install_paths.cpp index 8fc71fcfeee8..fab436a31301 100644 --- a/toolchain/install/install_paths.cpp +++ b/toolchain/install/install_paths.cpp @@ -225,6 +225,11 @@ auto InstallPaths::llvm_runtime_srcs() const -> std::filesystem::path { "/src"; } +auto InstallPaths::libunwind_path() const -> std::filesystem::path { + // TODO: Adjust this to work equally well on Windows. + return prefix_ / "lib/carbon/libunwind"; +} + auto InstallPaths::digest_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return prefix_ / "lib/carbon/install_digest.txt"; diff --git a/toolchain/install/install_paths.h b/toolchain/install/install_paths.h index 614befbb0ee9..9233db9a05c6 100644 --- a/toolchain/install/install_paths.h +++ b/toolchain/install/install_paths.h @@ -111,6 +111,9 @@ class InstallPaths { // The path to the root of LLVM runtime sources. auto llvm_runtime_srcs() const -> std::filesystem::path; + // The path to `libunwind` runtime. + auto libunwind_path() const -> std::filesystem::path; + // The installation digest path. // // This file contains a digest of the installation.