Pārlūkot izejas kodu

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.
Chandler Carruth 5 mēneši atpakaļ
vecāks
revīzija
3930fb13a5

+ 1 - 0
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)],

+ 34 - 0
bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch

@@ -0,0 +1,34 @@
+Commit ID: a79f1facf56cc5772557cdb09d811aa11a22e43b
+Change ID: sxspxmonsuvqzuvxvrvorlumwpwromsv
+Author   : Chandler Carruth <chandlerc@gmail.com> (2025-09-25 22:55:26)
+Committer: Chandler Carruth <chandlerc@gmail.com> (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",
++    ]),
++)

+ 17 - 1
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()
+    } | {
     },
 )
 

+ 1 - 0
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",

+ 4 - 0
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<Runtimes::LibUnwind> 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();
 }

+ 121 - 0
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 <Runtimes::Component Component>
+  requires(Component == Runtimes::LibUnwind)
+ClangArchiveRuntimesBuilder<Component>::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 <Runtimes::Component Component>
+  requires(Component == Runtimes::LibUnwind)
+auto ClangArchiveRuntimesBuilder<Component>::CollectSrcFiles()
+    -> llvm::SmallVector<llvm::StringRef> {
+  if constexpr (Component == Runtimes::LibUnwind) {
+    return llvm::SmallVector<llvm::StringRef>(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 <Runtimes::Component Component>
+  requires(Component == Runtimes::LibUnwind)
+auto ClangArchiveRuntimesBuilder<Component>::CollectCflags()
+    -> llvm::SmallVector<llvm::StringRef> {
+  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 <Runtimes::Component Component>
+  requires(Component == Runtimes::LibUnwind)
+auto ClangArchiveRuntimesBuilder<Component>::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 <Runtimes::Component Component>
+  requires(Component == Runtimes::LibUnwind)
+auto ClangArchiveRuntimesBuilder<Component>::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<Runtimes::LibUnwind>;
+
 ClangResourceDirBuilder::ClangResourceDirBuilder(
     ClangRunner* clang, llvm::ThreadPoolInterface* threads,
     llvm::Triple target_triple, Runtimes* runtimes)

+ 54 - 0
toolchain/driver/clang_runtimes.h

@@ -203,6 +203,60 @@ class ClangRuntimesBuilderBase::ArchiveBuilder {
   ErrorOr<Success> 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 <Runtimes::Component Component>
+  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<llvm::StringRef>;
+  auto CollectCflags() -> llvm::SmallVector<llvm::StringRef>;
+
+  // 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<ArchiveBuilder> archive_;
+};
+
+extern template class ClangArchiveRuntimesBuilder<Runtimes::LibUnwind>;
+
 // Builds the target-specific resource directory for Clang.
 //
 // There is a resource directory installed along side the Clang binary that

+ 3 - 0
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");
     }

+ 9 - 0
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,

+ 5 - 0
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";

+ 3 - 0
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.