rustdoc: Output target feature information
`#[target_feature]` attributes refer to a target-specific list of features. Enabling certain features can imply enabling other features. Certain features are always enabled on certain targets, since they are required by the target's ABI. Features can also be enabled indirectly based on other compiler flags. Feature information is ultimately known to `rustc`. Rather than force external tools to track it -- which may be wildly impractical due to `-C target-cpu` -- have `rustdoc` output `rustc`'s feature data.
This commit is contained in:
parent
51548ce71f
commit
8c50f95cf0
15 changed files with 257 additions and 2 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -176,6 +176,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",
|
||||
|
@ -189,6 +190,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",
|
||||
|
@ -220,6 +222,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",
|
||||
|
|
|
@ -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>.*)$
|
||||
"#,
|
||||
)
|
||||
|
|
|
@ -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, &[]);
|
||||
|
|
14
tests/rustdoc-json/targets/aarch64_apple_darwin.rs
Normal file
14
tests/rustdoc-json/targets/aarch64_apple_darwin.rs
Normal 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')]"
|
|
@ -0,0 +1,10 @@
|
|||
//@ 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=='neon')].globally_enabled" true
|
14
tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs
Normal file
14
tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
//@ only-aarch64-unknown-linux-gnu
|
||||
|
||||
//@ is "$.target.triple" \"aarch64-unknown-linux-gnu\"
|
||||
//@ is "$.target.target_features[?(@.name=='neon')].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')]"
|
14
tests/rustdoc-json/targets/i686_pc_windows_msvc.rs
Normal file
14
tests/rustdoc-json/targets/i686_pc_windows_msvc.rs
Normal 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')]"
|
14
tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs
Normal file
14
tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs
Normal 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')]"
|
14
tests/rustdoc-json/targets/x86_64_apple_darwin.rs
Normal file
14
tests/rustdoc-json/targets/x86_64_apple_darwin.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
//@ only-x86_64-apple-darwin
|
||||
|
||||
//@ is "$.target.triple" \"x86_64-apple-darwin\"
|
||||
//@ 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')]"
|
14
tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs
Normal file
14
tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs
Normal 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')]"
|
14
tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs
Normal file
14
tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs
Normal 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')]"
|
|
@ -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
|
14
tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs
Normal file
14
tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs
Normal 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')]"
|
Loading…
Add table
Add a link
Reference in a new issue