This commit is contained in:
Will Glynn 2025-04-13 13:27:19 +02:00 committed by GitHub
commit 80b8ed17a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 258 additions and 2 deletions

View File

@ -14,6 +14,7 @@ use std::io::{BufWriter, Write, stdout};
use std::path::PathBuf;
use std::rc::Rc;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@ -123,6 +124,58 @@ impl<'tcx> JsonRenderer<'tcx> {
}
}
fn target(sess: &rustc_session::Session) -> types::Target {
// Build a set of which features are enabled on this target
let globally_enabled_features: FxHashSet<&str> =
sess.unstable_target_features.iter().map(|name| name.as_str()).collect();
// Build a map of target feature stability by feature name
use rustc_target::target_features::Stability;
let feature_stability: FxHashMap<&str, Stability> = sess
.target
.rust_target_features()
.into_iter()
.copied()
.map(|(name, stability, _)| (name, stability))
.collect();
types::Target {
triple: sess.opts.target_triple.tuple().into(),
target_features: sess
.target
.rust_target_features()
.into_iter()
.copied()
.filter(|(_, stability, _)| {
// Describe only target features which the user can toggle
stability.toggle_allowed().is_ok()
})
.map(|(name, stability, implied_features)| {
types::TargetFeature {
name: name.into(),
unstable_feature_gate: match stability {
Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()),
_ => None,
},
implies_features: implied_features
.into_iter()
.copied()
.filter(|name| {
// Imply only target features which the user can toggle
feature_stability
.get(name)
.map(|stability| stability.toggle_allowed().is_ok())
.unwrap_or(false)
})
.map(String::from)
.collect(),
globally_enabled: globally_enabled_features.contains(name),
}
})
.collect(),
}
}
impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
fn descr() -> &'static str {
"json"
@ -248,6 +301,12 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
let e = ExternalCrate { crate_num: LOCAL_CRATE };
let index = (*self.index).clone().into_inner();
// Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for
// multiple targets: https://github.com/rust-lang/rust/pull/137632
//
// We want to describe a single target, so pass tcx.sess rather than tcx.
let target = target(self.tcx.sess);
debug!("Constructing Output");
let output_crate = types::Crate {
root: self.id_from_item_default(e.def_id().into()),
@ -288,6 +347,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
)
})
.collect(),
target,
format_version: types::FORMAT_VERSION,
};
if let Some(ref out_dir) = self.out_dir {

View File

@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
/// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`].
/// Consuming code should assert that this value matches the format version(s) that it supports.
pub const FORMAT_VERSION: u32 = 43;
pub const FORMAT_VERSION: u32 = 44;
/// The root of the emitted JSON blob.
///
@ -52,11 +52,67 @@ pub struct Crate {
pub paths: HashMap<Id, ItemSummary>,
/// Maps `crate_id` of items to a crate name and html_root_url if it exists.
pub external_crates: HashMap<u32, ExternalCrate>,
/// Information about the target for which this documentation was generated
pub target: Target,
/// A single version number to be used in the future when making backwards incompatible changes
/// to the JSON output.
pub format_version: u32,
}
/// Information about a target
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Target {
/// The target triple for which this documentation was generated
pub triple: String,
/// A list of features valid for use in `#[target_feature]` attributes
/// for the target where this rustdoc JSON was generated.
pub target_features: Vec<TargetFeature>,
}
/// Information about a target feature.
///
/// Rust target features are used to influence code generation, especially around selecting
/// instructions which are not universally supported by the target architecture.
///
/// Target features are commonly enabled by the [`#[target_feature]` attribute][1] to influence code
/// generation for a particular function, and less commonly enabled by compiler options like
/// `-Ctarget-feature` or `-Ctarget-cpu`. Targets themselves automatically enable certain target
/// features by default, for example because the target's ABI specification requires saving specific
/// registers which only exist in an architectural extension.
///
/// Target features can imply other target features: for example, x86-64 `avx2` implies `avx`, and
/// aarch64 `sve2` implies `sve`, since both of these architectural extensions depend on their
/// predecessors.
///
/// Target features can be probed at compile time by [`#[cfg(target_feature)]`][2] or `cfg!(…)`
/// conditional compilation to determine whether a target feature is enabled in a particular
/// context.
///
/// [1]: https://doc.rust-lang.org/stable/reference/attributes/codegen.html#the-target_feature-attribute
/// [2]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct TargetFeature {
/// The name of this target feature.
pub name: String,
/// Other target features which are implied by this target feature, if any.
pub implies_features: Vec<String>,
/// If this target feature is unstable, the name of the associated language feature gate.
pub unstable_feature_gate: Option<String>,
/// Whether this feature is globally enabled for this compilation session.
///
/// Target features can be globally enabled implicitly as a result of the target's definition.
/// For example, x86-64 hardware floating point ABIs require saving x87 and SSE2 registers,
/// which in turn requires globally enabling the `x87` and `sse2` target features so that the
/// generated machine code conforms to the target's ABI.
///
/// Target features can also be globally enabled explicitly as a result of compiler flags like
/// [`-Ctarget-feature`][1] or [`-Ctarget-cpu`][2].
///
/// [1]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-feature
/// [2]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-cpu
pub globally_enabled: bool,
}
/// Metadata of a crate, either the same crate on which `rustdoc` was invoked, or its dependency.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ExternalCrate {

View File

@ -177,6 +177,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-32bit",
"only-64bit",
"only-aarch64",
"only-aarch64-apple-darwin",
"only-aarch64-unknown-linux-gnu",
"only-apple",
"only-arm",
@ -190,6 +191,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-gnu",
"only-i686-pc-windows-gnu",
"only-i686-pc-windows-msvc",
"only-i686-unknown-linux-gnu",
"only-ios",
"only-linux",
"only-loongarch64",
@ -221,6 +223,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-windows-msvc",
"only-x86",
"only-x86_64",
"only-x86_64-apple-darwin",
"only-x86_64-fortanix-unknown-sgx",
"only-x86_64-pc-windows-gnu",
"only-x86_64-pc-windows-msvc",

View File

@ -156,7 +156,7 @@ static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
r#"
//@\s+
(?P<negated>!?)
(?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
(?P<args>.*)$
"#,
)

View File

@ -42,6 +42,7 @@ fn errors_on_missing_links() {
)]),
paths: FxHashMap::default(),
external_crates: FxHashMap::default(),
target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
format_version: rustdoc_json_types::FORMAT_VERSION,
};
@ -112,6 +113,7 @@ fn errors_on_local_in_paths_and_not_index() {
},
)]),
external_crates: FxHashMap::default(),
target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
format_version: rustdoc_json_types::FORMAT_VERSION,
};
@ -216,6 +218,7 @@ fn errors_on_missing_path() {
ItemSummary { crate_id: 0, path: vec!["foo".to_owned()], kind: ItemKind::Module },
)]),
external_crates: FxHashMap::default(),
target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
format_version: rustdoc_json_types::FORMAT_VERSION,
};
@ -259,6 +262,7 @@ fn checks_local_crate_id_is_correct() {
)]),
paths: FxHashMap::default(),
external_crates: FxHashMap::default(),
target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
format_version: FORMAT_VERSION,
};
check(&krate, &[]);

View File

@ -0,0 +1,14 @@
//@ only-aarch64-apple-darwin
//@ is "$.target.triple" \"aarch64-apple-darwin\"
//@ is "$.target.target_features[?(@.name=='vh')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='sve2')].implies_features" '["sve"]'
//@ is "$.target.target_features[?(@.name=='sve2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='cssc')].unstable_feature_gate" '"aarch64_unstable_target_feature"'
//@ is "$.target.target_features[?(@.name=='v9a')].unstable_feature_gate" '"aarch64_ver_target_feature"'
// Ensure we don't look like x86-64
//@ !has "$.target.target_features[?(@.name=='avx2')]"

View File

@ -0,0 +1,11 @@
//@ only-aarch64
// If we enable SVE Bit Permute, we should see that it is enabled
//@ compile-flags: -Ctarget-feature=+sve2-bitperm
//@ is "$.target.target_features[?(@.name=='sve2-bitperm')].globally_enabled" true
// As well as its dependency chain
//@ is "$.target.target_features[?(@.name=='sve2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='fp16')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='neon')].globally_enabled" true

View File

@ -0,0 +1,14 @@
//@ only-aarch64-unknown-linux-gnu
//@ is "$.target.triple" \"aarch64-unknown-linux-gnu\"
//@ is "$.target.target_features[?(@.name=='vh')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='sve2')].implies_features" '["sve"]'
//@ is "$.target.target_features[?(@.name=='sve2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='cssc')].unstable_feature_gate" '"aarch64_unstable_target_feature"'
//@ is "$.target.target_features[?(@.name=='v9a')].unstable_feature_gate" '"aarch64_ver_target_feature"'
// Ensure we don't look like x86-64
//@ !has "$.target.target_features[?(@.name=='avx2')]"

View File

@ -0,0 +1,14 @@
//@ only-i686-pc-windows-msvc
//@ is "$.target.triple" \"i686-pc-windows-msvc\"
//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"

View File

@ -0,0 +1,14 @@
//@ only-i686-unknown-linux-gnu
//@ is "$.target.triple" \"i686-unknown-linux-gnu\"
//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"

View File

@ -0,0 +1,14 @@
//@ only-x86_64-apple-darwin
//@ is "$.target.triple" \"x86_64-apple-darwin\"
//@ is "$.target.target_features[?(@.name=='?')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='?')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"

View File

@ -0,0 +1,14 @@
//@ only-x86_64-pc-windows-gnu
//@ is "$.target.triple \"x86_64-pc-windows-gnu\"
//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"

View File

@ -0,0 +1,14 @@
//@ only-x86_64-pc-windows-msvc
//@ is "$.target.triple" \"x86_64-pc-windows-msvc\"
//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"

View File

@ -0,0 +1,10 @@
//@ only-x86_64
// If we enable AVX2, we should see that it is enabled
//@ compile-flags: -Ctarget-feature=+avx2
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" true
// As well as its dependency chain
//@ is "$.target.target_features[?(@.name=='avx')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='sse4.2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='sse4.1')].globally_enabled" true

View File

@ -0,0 +1,14 @@
//@ only-x86_64-unknown-linux-gnu
//@ is "$.target.triple" \"x86_64-unknown-linux-gnu\"
//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
// If this breaks due to stabilization, check rustc_target::target_features for a replacement
//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
// Ensure we don't look like aarch64
//@ !has "$.target.target_features[?(@.name=='sve2')]"