2022-08-04 10:20:37 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
//! The custom target specification file generator for `rustc`.
|
|
|
|
//!
|
|
|
|
//! To configure a target from scratch, a JSON-encoded file has to be passed
|
|
|
|
//! to `rustc` (introduced in [RFC 131]). These options and the file itself are
|
|
|
|
//! unstable. Eventually, `rustc` should provide a way to do this in a stable
|
|
|
|
//! manner. For instance, via command-line arguments. Therefore, this file
|
|
|
|
//! should avoid using keys which can be set via `-C` or `-Z` options.
|
|
|
|
//!
|
|
|
|
//! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
fmt::{Display, Formatter, Result},
|
|
|
|
io::BufRead,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum Value {
|
|
|
|
Boolean(bool),
|
|
|
|
Number(i32),
|
|
|
|
String(String),
|
|
|
|
Object(Object),
|
|
|
|
}
|
|
|
|
|
|
|
|
type Object = Vec<(String, Value)>;
|
|
|
|
|
|
|
|
/// Minimal "almost JSON" generator (e.g. no `null`s, no arrays, no escaping),
|
|
|
|
/// enough for this purpose.
|
|
|
|
impl Display for Value {
|
|
|
|
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
|
|
|
|
match self {
|
|
|
|
Value::Boolean(boolean) => write!(formatter, "{}", boolean),
|
|
|
|
Value::Number(number) => write!(formatter, "{}", number),
|
|
|
|
Value::String(string) => write!(formatter, "\"{}\"", string),
|
|
|
|
Value::Object(object) => {
|
|
|
|
formatter.write_str("{")?;
|
|
|
|
if let [ref rest @ .., ref last] = object[..] {
|
|
|
|
for (key, value) in rest {
|
|
|
|
write!(formatter, "\"{}\": {},", key, value)?;
|
|
|
|
}
|
|
|
|
write!(formatter, "\"{}\": {}", last.0, last.1)?;
|
|
|
|
}
|
|
|
|
formatter.write_str("}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TargetSpec(Object);
|
|
|
|
|
|
|
|
impl TargetSpec {
|
|
|
|
fn new() -> TargetSpec {
|
|
|
|
TargetSpec(Vec::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait Push<T> {
|
|
|
|
fn push(&mut self, key: &str, value: T);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Push<bool> for TargetSpec {
|
|
|
|
fn push(&mut self, key: &str, value: bool) {
|
|
|
|
self.0.push((key.to_string(), Value::Boolean(value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Push<i32> for TargetSpec {
|
|
|
|
fn push(&mut self, key: &str, value: i32) {
|
|
|
|
self.0.push((key.to_string(), Value::Number(value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Push<String> for TargetSpec {
|
|
|
|
fn push(&mut self, key: &str, value: String) {
|
|
|
|
self.0.push((key.to_string(), Value::String(value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Push<&str> for TargetSpec {
|
|
|
|
fn push(&mut self, key: &str, value: &str) {
|
|
|
|
self.push(key, value.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Push<Object> for TargetSpec {
|
|
|
|
fn push(&mut self, key: &str, value: Object) {
|
|
|
|
self.0.push((key.to_string(), Value::Object(value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for TargetSpec {
|
|
|
|
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
|
|
|
|
// We add some newlines for clarity.
|
|
|
|
formatter.write_str("{\n")?;
|
|
|
|
if let [ref rest @ .., ref last] = self.0[..] {
|
|
|
|
for (key, value) in rest {
|
|
|
|
write!(formatter, " \"{}\": {},\n", key, value)?;
|
|
|
|
}
|
|
|
|
write!(formatter, " \"{}\": {}\n", last.0, last.1)?;
|
|
|
|
}
|
|
|
|
formatter.write_str("}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct KernelConfig(HashMap<String, String>);
|
|
|
|
|
|
|
|
impl KernelConfig {
|
|
|
|
/// Parses `include/config/auto.conf` from `stdin`.
|
|
|
|
fn from_stdin() -> KernelConfig {
|
|
|
|
let mut result = HashMap::new();
|
|
|
|
|
|
|
|
let stdin = std::io::stdin();
|
|
|
|
let mut handle = stdin.lock();
|
|
|
|
let mut line = String::new();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
line.clear();
|
|
|
|
|
|
|
|
if handle.read_line(&mut line).unwrap() == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if line.starts_with('#') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let (key, value) = line.split_once('=').expect("Missing `=` in line.");
|
|
|
|
result.insert(key.to_string(), value.trim_end_matches('\n').to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
KernelConfig(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Does the option exist in the configuration (any value)?
|
|
|
|
///
|
|
|
|
/// The argument must be passed without the `CONFIG_` prefix.
|
|
|
|
/// This avoids repetition and it also avoids `fixdep` making us
|
|
|
|
/// depend on it.
|
|
|
|
fn has(&self, option: &str) -> bool {
|
|
|
|
let option = "CONFIG_".to_owned() + option;
|
|
|
|
self.0.contains_key(&option)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let cfg = KernelConfig::from_stdin();
|
|
|
|
let mut ts = TargetSpec::new();
|
|
|
|
|
|
|
|
// `llvm-target`s are taken from `scripts/Makefile.clang`.
|
2023-10-20 15:50:56 +00:00
|
|
|
if cfg.has("ARM64") {
|
|
|
|
panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
|
2024-04-09 17:25:16 +00:00
|
|
|
} else if cfg.has("RISCV") {
|
|
|
|
if cfg.has("64BIT") {
|
|
|
|
panic!("64-bit RISC-V uses the builtin rustc riscv64-unknown-none-elf target");
|
|
|
|
} else {
|
|
|
|
panic!("32-bit RISC-V is an unsupported architecture");
|
|
|
|
}
|
2023-10-20 15:50:56 +00:00
|
|
|
} else if cfg.has("X86_64") {
|
2022-08-04 10:16:44 +00:00
|
|
|
ts.push("arch", "x86_64");
|
|
|
|
ts.push(
|
|
|
|
"data-layout",
|
rust: upgrade to Rust 1.78.0
This is the next upgrade to the Rust toolchain, from 1.77.1 to 1.78.0
(i.e. the latest) [1].
See the upgrade policy [2] and the comments on the first upgrade in
commit 3ed03f4da06e ("rust: upgrade to Rust 1.68.2").
It is much smaller than previous upgrades, since the `alloc` fork was
dropped in commit 9d0441bab775 ("rust: alloc: remove our fork of the
`alloc` crate") [3].
# Unstable features
There have been no changes to the set of unstable features used in
our own code. Therefore, the only unstable features allowed to be used
outside the `kernel` crate is still `new_uninit`.
However, since we finally dropped our `alloc` fork [3], all the unstable
features used by `alloc` (~30 language ones, ~60 library ones) are not
a concern anymore. This reduces the maintenance burden, increases the
chances of new compiler versions working without changes and gets us
closer to the goal of supporting several compiler versions.
It also means that, ignoring non-language/library features, we are
currently left with just the few language features needed to implement the
kernel `Arc`, the `new_uninit` library feature, the `compiler_builtins`
marker and the few `no_*` `cfg`s we pass when compiling `core`/`alloc`.
Please see [4] for details.
# Required changes
## LLVM's data layout
Rust 1.77.0 (i.e. the previous upgrade) introduced a check for matching
LLVM data layouts [5]. Then, Rust 1.78.0 upgraded LLVM's bundled major
version from 17 to 18 [6], which changed the data layout in x86 [7]. Thus
update the data layout in our custom target specification for x86 so
that the compiler does not complain about the mismatch:
error: data-layout for target `target-5559158138856098584`,
`e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`,
differs from LLVM target's `x86_64-linux-gnu` default layout,
`e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128`
In the future, the goal is to drop the custom target specifications.
Meanwhile, if we want to support other LLVM versions used in `rustc`
(e.g. for LTO), we will need to add some extra logic (e.g. conditional on
LLVM's version, or extracting the data layout from an existing built-in
target specification).
## `unused_imports`
Rust's `unused_imports` lint covers both unused and redundant imports.
Now, in 1.78.0, the lint detects more cases of redundant imports [8].
Thus one of the previous patches cleaned them up.
## Clippy's `new_without_default`
Clippy now suggests to implement `Default` even when `new()` is `const`,
since `Default::default()` may call `const` functions even if it is not
`const` itself [9]. Thus one of the previous patches implemented it.
# Other changes in Rust
Rust 1.78.0 introduced `feature(asm_goto)` [10] [11]. This feature was
discussed in the past [12].
Rust 1.78.0 introduced `feature(const_refs_to_static)` [13] to allow
referencing statics in constants and extended `feature(const_mut_refs)`
to allow raw mutable pointers in constants. Together, this should cover
the kernel's `VTABLE` use case. In fact, the implementation [14] in
upstream Rust added a test case for it [15].
Rust 1.78.0 with debug assertions enabled (i.e. `-Cdebug-assertions=y`,
kernel's `CONFIG_RUST_DEBUG_ASSERTIONS=y`) now always checks all unsafe
preconditions, though without a way to opt-out for particular cases [16].
It would be ideal to have a way to selectively disable certain checks
per-call site for this one (i.e. not just per check but for particular
instances of a check), even if the vast majority of the checks remain
in place [17].
Rust 1.78.0 also improved a couple issues we reported when giving feedback
for the new `--check-cfg` feature [18] [19].
# `alloc` upgrade and reviewing
As mentioned above, compiler upgrades will not update `alloc` anymore,
since we dropped our `alloc` fork [3].
Link: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1780-2024-05-02 [1]
Link: https://rust-for-linux.com/rust-version-policy [2]
Link: https://lore.kernel.org/rust-for-linux/20240328013603.206764-1-wedsonaf@gmail.com/ [3]
Link: https://github.com/Rust-for-Linux/linux/issues/2 [4]
Link: https://github.com/rust-lang/rust/pull/120062 [5]
Link: https://github.com/rust-lang/rust/pull/120055 [6]
Link: https://reviews.llvm.org/D86310 [7]
Link: https://github.com/rust-lang/rust/pull/117772 [8]
Link: https://github.com/rust-lang/rust-clippy/pull/10903 [9]
Link: https://github.com/rust-lang/rust/pull/119365 [10]
Link: https://github.com/rust-lang/rust/issues/119364 [11]
Link: https://lore.kernel.org/rust-for-linux/ZWipTZysC2YL7qsq@Boquns-Mac-mini.home/ [12]
Link: https://github.com/rust-lang/rust/issues/119618 [13]
Link: https://github.com/rust-lang/rust/pull/120932 [14]
Link: https://github.com/rust-lang/rust/pull/120932/files#diff-e6fc1622c46054cd46b1d225c5386c5554564b3b0fa8a03c2dc2d8627a1079d9 [15]
Link: https://github.com/rust-lang/rust/issues/120969 [16]
Link: https://github.com/Rust-for-Linux/linux/issues/354 [17]
Link: https://github.com/rust-lang/rust/pull/121202 [18]
Link: https://github.com/rust-lang/rust/pull/121237 [19]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20240401212303.537355-4-ojeda@kernel.org
[ Added a few more details and links I mentioned in the list. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2024-04-01 21:23:03 +00:00
|
|
|
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
2022-08-04 10:16:44 +00:00
|
|
|
);
|
rust: x86: remove `-3dnow{,a}` from target features
LLVM 19 is dropping support for 3DNow! in commit f0eb5587ceeb ("Remove
support for 3DNow!, both intrinsics and builtins. (#96246)"):
Remove support for 3DNow!, both intrinsics and builtins. (#96246)
This set of instructions was only supported by AMD chips starting in
the K6-2 (introduced 1998), and before the "Bulldozer" family
(2011). They were never much used, as they were effectively superseded
by the more-widely-implemented SSE (first implemented on the AMD side
in Athlon XP in 2001).
This is being done as a predecessor towards general removal of MMX
register usage. Since there is almost no usage of the 3DNow!
intrinsics, and no modern hardware even implements them, simple
removal seems like the best option.
Thus we should avoid passing these to the backend, since otherwise we
get a diagnostic about it:
'-3dnow' is not a recognized feature for this target (ignoring feature)
'-3dnowa' is not a recognized feature for this target (ignoring feature)
We could try to disable them only up to LLVM 19 (not the C side one,
but the one used by `rustc`, which may be built with a range of
LLVMs). However, to avoid more complexity, we can likely just remove
them altogether. According to Nikita [2]:
> I don't think it's needed because LLVM should not generate 3dnow
> instructions unless specifically asked to, using intrinsics that
> Rust does not provide in the first place.
Thus do so, like Rust did for one of their builtin targets [3].
For those curious: Clang will warn only about trying to enable them
(`-m3dnow{,a}`), but not about disabling them (`-mno-3dnow{,a}`), so
there is no change needed there.
Cc: Nikita Popov <github@npopov.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: x86@kernel.org
Link: https://github.com/llvm/llvm-project/commit/f0eb5587ceeb641445b64cb264c822b4751de04a [1]
Link: https://github.com/rust-lang/rust/pull/127864#issuecomment-2235898760 [2]
Link: https://github.com/rust-lang/rust/pull/127864 [3]
Closes: https://github.com/Rust-for-Linux/linux/issues/1094
Tested-by: Benno Lossin <benno.lossin@proton.me>
Tested-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20240806144558.114461-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2024-08-06 14:45:58 +00:00
|
|
|
let mut features = "-mmx,+soft-float".to_string();
|
2023-11-21 16:07:32 +00:00
|
|
|
if cfg.has("MITIGATION_RETPOLINE") {
|
2022-08-04 10:16:44 +00:00
|
|
|
features += ",+retpoline-external-thunk";
|
|
|
|
}
|
|
|
|
ts.push("features", features);
|
|
|
|
ts.push("llvm-target", "x86_64-linux-gnu");
|
|
|
|
ts.push("target-pointer-width", "64");
|
arch: um: rust: Add i386 support for Rust
At present, Rust in the kernel only supports 64-bit x86, so UML has
followed suit. However, it's significantly easier to support 32-bit i386
on UML than on bare metal, as UML does not use the -mregparm option
(which alters the ABI), which is not yet supported by rustc[1].
Add support for CONFIG_RUST on um/i386, by adding a new target config to
generate_rust_target, and replacing various checks on CONFIG_X86_64 to
also support CONFIG_X86_32.
We still use generate_rust_target, rather than a built-in rustc target,
in order to match x86_64, provide a future place for -mregparm, and more
easily disable floating point instructions.
With these changes, the KUnit tests pass with:
kunit.py run --make_options LLVM=1 --kconfig_add CONFIG_RUST=y
--kconfig_add CONFIG_64BIT=n --kconfig_add CONFIG_FORTIFY_SOURCE=n
An earlier version of these changes was proposed on the Rust-for-Linux
github[2].
[1]: https://github.com/rust-lang/rust/issues/116972
[2]: https://github.com/Rust-for-Linux/linux/pull/966
Signed-off-by: David Gow <davidgow@google.com>
Link: https://patch.msgid.link/20240604224052.3138504-1-davidgow@google.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2024-06-04 22:40:50 +00:00
|
|
|
} else if cfg.has("X86_32") {
|
|
|
|
// This only works on UML, as i386 otherwise needs regparm support in rustc
|
|
|
|
if !cfg.has("UML") {
|
|
|
|
panic!("32-bit x86 only works under UML");
|
|
|
|
}
|
|
|
|
ts.push("arch", "x86");
|
|
|
|
ts.push(
|
|
|
|
"data-layout",
|
|
|
|
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
|
|
|
);
|
rust: x86: remove `-3dnow{,a}` from target features
LLVM 19 is dropping support for 3DNow! in commit f0eb5587ceeb ("Remove
support for 3DNow!, both intrinsics and builtins. (#96246)"):
Remove support for 3DNow!, both intrinsics and builtins. (#96246)
This set of instructions was only supported by AMD chips starting in
the K6-2 (introduced 1998), and before the "Bulldozer" family
(2011). They were never much used, as they were effectively superseded
by the more-widely-implemented SSE (first implemented on the AMD side
in Athlon XP in 2001).
This is being done as a predecessor towards general removal of MMX
register usage. Since there is almost no usage of the 3DNow!
intrinsics, and no modern hardware even implements them, simple
removal seems like the best option.
Thus we should avoid passing these to the backend, since otherwise we
get a diagnostic about it:
'-3dnow' is not a recognized feature for this target (ignoring feature)
'-3dnowa' is not a recognized feature for this target (ignoring feature)
We could try to disable them only up to LLVM 19 (not the C side one,
but the one used by `rustc`, which may be built with a range of
LLVMs). However, to avoid more complexity, we can likely just remove
them altogether. According to Nikita [2]:
> I don't think it's needed because LLVM should not generate 3dnow
> instructions unless specifically asked to, using intrinsics that
> Rust does not provide in the first place.
Thus do so, like Rust did for one of their builtin targets [3].
For those curious: Clang will warn only about trying to enable them
(`-m3dnow{,a}`), but not about disabling them (`-mno-3dnow{,a}`), so
there is no change needed there.
Cc: Nikita Popov <github@npopov.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: x86@kernel.org
Link: https://github.com/llvm/llvm-project/commit/f0eb5587ceeb641445b64cb264c822b4751de04a [1]
Link: https://github.com/rust-lang/rust/pull/127864#issuecomment-2235898760 [2]
Link: https://github.com/rust-lang/rust/pull/127864 [3]
Closes: https://github.com/Rust-for-Linux/linux/issues/1094
Tested-by: Benno Lossin <benno.lossin@proton.me>
Tested-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20240806144558.114461-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2024-08-06 14:45:58 +00:00
|
|
|
let mut features = "-mmx,+soft-float".to_string();
|
arch: um: rust: Add i386 support for Rust
At present, Rust in the kernel only supports 64-bit x86, so UML has
followed suit. However, it's significantly easier to support 32-bit i386
on UML than on bare metal, as UML does not use the -mregparm option
(which alters the ABI), which is not yet supported by rustc[1].
Add support for CONFIG_RUST on um/i386, by adding a new target config to
generate_rust_target, and replacing various checks on CONFIG_X86_64 to
also support CONFIG_X86_32.
We still use generate_rust_target, rather than a built-in rustc target,
in order to match x86_64, provide a future place for -mregparm, and more
easily disable floating point instructions.
With these changes, the KUnit tests pass with:
kunit.py run --make_options LLVM=1 --kconfig_add CONFIG_RUST=y
--kconfig_add CONFIG_64BIT=n --kconfig_add CONFIG_FORTIFY_SOURCE=n
An earlier version of these changes was proposed on the Rust-for-Linux
github[2].
[1]: https://github.com/rust-lang/rust/issues/116972
[2]: https://github.com/Rust-for-Linux/linux/pull/966
Signed-off-by: David Gow <davidgow@google.com>
Link: https://patch.msgid.link/20240604224052.3138504-1-davidgow@google.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2024-06-04 22:40:50 +00:00
|
|
|
if cfg.has("MITIGATION_RETPOLINE") {
|
|
|
|
features += ",+retpoline-external-thunk";
|
|
|
|
}
|
|
|
|
ts.push("features", features);
|
|
|
|
ts.push("llvm-target", "i386-unknown-linux-gnu");
|
|
|
|
ts.push("target-pointer-width", "32");
|
2024-01-17 04:43:00 +00:00
|
|
|
} else if cfg.has("LOONGARCH") {
|
2024-05-14 04:24:18 +00:00
|
|
|
panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target");
|
2022-08-04 10:20:37 +00:00
|
|
|
} else {
|
|
|
|
panic!("Unsupported architecture");
|
|
|
|
}
|
|
|
|
|
|
|
|
ts.push("emit-debug-gdb-scripts", false);
|
|
|
|
ts.push("frame-pointer", "may-omit");
|
|
|
|
ts.push(
|
|
|
|
"stack-probes",
|
|
|
|
vec![("kind".to_string(), Value::String("none".to_string()))],
|
|
|
|
);
|
|
|
|
|
|
|
|
// Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not
|
|
|
|
// (e.g. x86). It is also `rustc`'s default.
|
|
|
|
if cfg.has("CPU_BIG_ENDIAN") {
|
|
|
|
ts.push("target-endian", "big");
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("{}", ts);
|
|
|
|
}
|