Files
carbon-lang/toolchain/driver/carbon_runtimes.cpp
Chandler Carruth 0546b48a7d Support --target-cpu, --target-cpu-tune, and --target-cpu-features (#7903)
Add support to the driver for CLI flags that select which CPU to target,
which CPU to tune performance for, and which CPU features to enable.

- `--target-cpu` selects the architecture of CPU to target, similar to
`-march` on x86. This combines `-march` with `armv*` strings and `-mcpu`
on Arm for specific CPU names. Similarly for RISC-V (if/when we support
and test it), etc. Notably, `--target-cpu=native` targets the current
native CPU.
- `--target-cpu-tune` maps to `-mtune`. This is expected to be rarely if
ever desirable, and I'm open to removing it, but if folks are relying on
this for performance I didn't want to hide the option.
- `--target-cpu-features` allows a "standard" (for LLVM) comma-separated
list of LLVM subtarget features, including `+feature` and `-feature`
syntax.

These flags, when provided, establish the default baseline for CPU
targeting and control how on-demand runtimes are built. They can be
changed or overridden by additional Clang flags when using
`--clang-arg`.

A critical part of this is building runtimes on demand using these
features. For that, we need to do some amount of canonicalization
(re-using Clang's canonicalization to ensure consistent behavior), and
extend the runtimes cache features to recognize these customizations.
Note that only the top-level Carbon flags impact runtimes, any
particular Clang arguments are only used by the specific compiles that
receive those arguments.

There is some nascent RISC-V support here, but it is incomplete and only
minimally tested. Still, it seemed better to record the parts we could
test than remove them.

Assisted-by: Antigravity with Gemini
2026-10-21 00:43:54 +00:00

119 lines
4.9 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/driver/carbon_runtimes.h"
#include "toolchain/base/install_paths.h"
#include "toolchain/base/runtimes_build_info.h"
namespace Carbon {
CarbonRuntimesBuilderBase::CarbonRuntimesBuilderBase(
DriverEnv* driver_env, const CodegenOptions* codegen_options)
: compile_options_(codegen_options),
compile_driver_(&compile_options_),
driver_env_(driver_env),
install_root_(driver_env->installation->root()),
result_(Error("Did not finish building the Carbon runtimes!")) {
// Each prelude file explicitly imports the other parts of the prelude it
// needs.
compile_options_.prelude_import = false;
// Don't also try and compile the rest of the core when compiling the prelude.
compile_options_.include_carbon_core = false;
}
CarbonPreludeBuilder::CarbonPreludeBuilder(
DriverEnv* driver_env, Runtimes* runtimes,
const CodegenOptions* codegen_options)
: CarbonRuntimesBuilderBase(driver_env, codegen_options) {
auto build_dir_or_error = runtimes->Build(Runtimes::CarbonCore);
if (!build_dir_or_error.ok()) {
result_ = std::move(build_dir_or_error).error();
return;
}
auto build_dir = *std::move(build_dir_or_error);
if (std::holds_alternative<std::filesystem::path>(build_dir)) {
// Reuse cached build.
result_ = std::get<std::filesystem::path>(std::move(build_dir));
return;
}
runtimes_builder_ = std::get<Runtimes::Builder>(std::move(build_dir));
lib_path_ = std::filesystem::path("lib/core") /
compile_options_.codegen_options->target.str();
}
auto CarbonPreludeBuilder::Build() && -> ErrorOr<std::filesystem::path> {
// If we didn't make a Builder in the constructor we either encountered an
// error or an already cached build, return the result.
if (!runtimes_builder_) {
return std::move(result_);
}
// Create the output directory.
auto lib_dir_result = runtimes_builder_->dir().CreateDirectories(lib_path_);
if (!lib_dir_result.ok()) {
return std::move(lib_dir_result).error();
}
auto lib_dir = *std::move(lib_dir_result);
// Gather the absolute paths to the prelude source files into a string list.
// This serves as a backing store for the `llvm::StringRef` inputs required to
// the `CompileDriver` via `compile_options_`.
llvm::SmallVector<std::string> prelude_source_files;
llvm::for_each(RuntimesBuildInfo::CarbonCorePreludeSrcs,
[&](llvm::StringRef src) -> void {
prelude_source_files.emplace_back(
(install_root_ / std::filesystem::path(src.str())));
compile_options_.input_filenames.emplace_back(
llvm::StringRef(prelude_source_files.back()));
});
auto install_root_length = install_root_.string().length();
auto init_result = compile_driver_.Initialize(
*driver_env_, [&](llvm::StringRef input_filename) -> std::string {
// Make the input path relative to the input path root, and
// replace the `.carbon` extension with a `.o`, so we can re-parent
// this file and any subdirectories into the output path.
auto relative_output_path =
lib_path_ / std::filesystem::path(
input_filename.substr(install_root_length).str())
.replace_extension(".o");
// The output path may contain subdirectories that haven't yet been
// created, so create if necessary.
// **Note** this call to `Dir::CreateDirectory` requires a path
// _relative_to_ where the `Dir` object itself was opened, which the
// runtimes builder opened at the path represented by
// `runtimes_builder_->path()`. So we use the _relative_ path here.
// The Builder exposes both `dir()` and `path()` methods because
// `Dir` objects themselves don't know their own path, so the
// Builder presents it separately.
auto mkdir_result = runtimes_builder_->dir().CreateDirectories(
relative_output_path.parent_path());
auto absolute_output_path =
runtimes_builder_->path() / relative_output_path;
CARBON_CHECK(
mkdir_result.ok(),
"Failed to make output subdirectory {0} while building Carbon "
"prelude.",
absolute_output_path.parent_path());
return absolute_output_path.string();
});
if (!init_result) {
return Error("Failed to initialize compiler driver for Carbon prelude.");
}
auto compile_result = compile_driver_.Compile(*driver_env_);
if (!compile_result.success) {
return Error("Failed to compile Carbon prelude.");
}
result_ = (*std::move(runtimes_builder_)).Commit();
return std::move(result_);
}
} // namespace Carbon