rustc_target/
target_features.rs

1//! Declares Rust's target feature names for each target.
2//! Note that these are similar to but not always identical to LLVM's feature names,
3//! and Rust adds some features that do not correspond to LLVM features at all.
4use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
6use rustc_span::{Symbol, sym};
7
8use crate::spec::{FloatAbi, RustcAbi, Target};
9
10/// Features that control behaviour of rustc, rather than the codegen.
11/// These exist globally and are not in the target-specific lists below.
12pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
13
14/// Features that require special handling when passing to LLVM:
15/// these are target-specific (i.e., must also be listed in the target-specific list below)
16/// but do not correspond to an LLVM target feature.
17pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
18
19/// Stability information for target features.
20#[derive(Debug, Copy, Clone)]
21pub enum Stability {
22    /// This target feature is stable, it can be used in `#[target_feature]` and
23    /// `#[cfg(target_feature)]`.
24    Stable,
25    /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on
26    /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature.
27    Unstable(
28        /// This must be a *language* feature, or else rustc will ICE when reporting a missing
29        /// feature gate!
30        Symbol,
31    ),
32    /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be
33    /// set in the target spec. It is never set in `cfg(target_feature)`. Used in
34    /// particular for features are actually ABI configuration flags (not all targets are as nice as
35    /// RISC-V and have an explicit way to set the ABI separate from target features).
36    Forbidden { reason: &'static str },
37}
38use Stability::*;
39
40impl<CTX> HashStable<CTX> for Stability {
41    #[inline]
42    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
43        std::mem::discriminant(self).hash_stable(hcx, hasher);
44        match self {
45            Stability::Stable => {}
46            Stability::Unstable(nightly_feature) => {
47                nightly_feature.hash_stable(hcx, hasher);
48            }
49            Stability::Forbidden { reason } => {
50                reason.hash_stable(hcx, hasher);
51            }
52        }
53    }
54}
55
56impl Stability {
57    /// Returns whether the feature can be used in `cfg(target_feature)` ever.
58    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
59    /// `requires_nightly`.)
60    pub fn in_cfg(&self) -> bool {
61        !matches!(self, Stability::Forbidden { .. })
62    }
63
64    /// Returns the nightly feature that is required to toggle this target feature via
65    /// `#[target_feature]`/`-Ctarget-feature` or to test it via `cfg(target_feature)`.
66    /// (For `cfg` we only care whether the feature is nightly or not, we don't require
67    /// the feature gate to actually be enabled when using a nightly compiler.)
68    ///
69    /// Before calling this, ensure the feature is even permitted for this use:
70    /// - for `#[target_feature]`/`-Ctarget-feature`, check `allow_toggle()`
71    /// - for `cfg(target_feature)`, check `in_cfg`
72    pub fn requires_nightly(&self) -> Option<Symbol> {
73        match *self {
74            Stability::Unstable(nightly_feature) => Some(nightly_feature),
75            Stability::Stable { .. } => None,
76            Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
77        }
78    }
79
80    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
81    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
82    /// `requires_nightly`.)
83    pub fn toggle_allowed(&self) -> Result<(), &'static str> {
84        match self {
85            Stability::Forbidden { reason } => Err(reason),
86            _ => Ok(()),
87        }
88    }
89}
90
91// Here we list target features that rustc "understands": they can be used in `#[target_feature]`
92// and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with
93// `-Ctarget-feature`.
94//
95// Note that even unstable (and even entirely unlisted) features can be used with `-Ctarget-feature`
96// on stable. Using a feature not on the list of Rust target features only emits a warning.
97// Only `cfg(target_feature)` and `#[target_feature]` actually do any stability gating.
98// `cfg(target_feature)` for unstable features just works on nightly without any feature gate.
99// `#[target_feature]` requires a feature gate.
100//
101// When adding features to the below lists
102// check whether they're named already elsewhere in rust
103// e.g. in stdarch and whether the given name matches LLVM's
104// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted.
105//
106// Also note that all target features listed here must be purely additive: for target_feature 1.1 to
107// be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a
108// per-function level, since we would then allow safe calls from functions with `+soft-float` to
109// functions without that feature!
110//
111// It is important for soundness to consider the interaction of targets features and the function
112// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as
113// arguments, so letting people toggle that feature would be unsound. To this end, the
114// `abi_required_features` function computes which target features must and must not be enabled for
115// any given target, and individual features can also be marked as `Forbidden`.
116// See https://github.com/rust-lang/rust/issues/116344 for some more context.
117//
118// The one exception to features that change the ABI is features that enable larger vector
119// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store
120// information about which target feature is ABI-required for which vector size; this is used to
121// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For
122// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.)
123// Also see https://github.com/rust-lang/rust/issues/116558.
124//
125// Stabilizing a target feature requires t-lang approval.
126
127// If feature A "implies" feature B, then:
128// - when A gets enabled (via `-Ctarget-feature` or `#[target_feature]`), we also enable B
129// - when B gets disabled (via `-Ctarget-feature`), we also disable A
130//
131// Both of these are also applied transitively.
132type ImpliedFeatures = &'static [&'static str];
133
134static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
135    // tidy-alphabetical-start
136    ("aclass", Unstable(sym::arm_target_feature), &[]),
137    ("aes", Unstable(sym::arm_target_feature), &["neon"]),
138    (
139        "atomics-32",
140        Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" },
141        &[],
142    ),
143    ("crc", Unstable(sym::arm_target_feature), &[]),
144    ("d32", Unstable(sym::arm_target_feature), &[]),
145    ("dotprod", Unstable(sym::arm_target_feature), &["neon"]),
146    ("dsp", Unstable(sym::arm_target_feature), &[]),
147    ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]),
148    ("fp16", Unstable(sym::arm_target_feature), &["neon"]),
149    ("fpregs", Unstable(sym::arm_target_feature), &[]),
150    ("i8mm", Unstable(sym::arm_target_feature), &["neon"]),
151    ("mclass", Unstable(sym::arm_target_feature), &[]),
152    ("neon", Unstable(sym::arm_target_feature), &["vfp3"]),
153    ("rclass", Unstable(sym::arm_target_feature), &[]),
154    ("sha2", Unstable(sym::arm_target_feature), &["neon"]),
155    // This can be *disabled* on non-`hf` targets to enable the use
156    // of hardfloats while keeping the softfloat ABI.
157    // FIXME before stabilization: Should we expose this as a `hard-float` target feature instead of
158    // matching the odd negative feature LLVM uses?
159    ("soft-float", Unstable(sym::arm_target_feature), &[]),
160    // This is needed for inline assembly, but shouldn't be stabilized as-is
161    // since it should be enabled per-function using #[instruction_set], not
162    // #[target_feature].
163    ("thumb-mode", Unstable(sym::arm_target_feature), &[]),
164    ("thumb2", Unstable(sym::arm_target_feature), &[]),
165    ("trustzone", Unstable(sym::arm_target_feature), &[]),
166    ("v5te", Unstable(sym::arm_target_feature), &[]),
167    ("v6", Unstable(sym::arm_target_feature), &["v5te"]),
168    ("v6k", Unstable(sym::arm_target_feature), &["v6"]),
169    ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]),
170    ("v7", Unstable(sym::arm_target_feature), &["v6t2"]),
171    ("v8", Unstable(sym::arm_target_feature), &["v7"]),
172    ("vfp2", Unstable(sym::arm_target_feature), &[]),
173    ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]),
174    ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]),
175    ("virtualization", Unstable(sym::arm_target_feature), &[]),
176    // tidy-alphabetical-end
177];
178
179static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
180    // tidy-alphabetical-start
181    // FEAT_AES & FEAT_PMULL
182    ("aes", Stable, &["neon"]),
183    // FEAT_BF16
184    ("bf16", Stable, &[]),
185    // FEAT_BTI
186    ("bti", Stable, &[]),
187    // FEAT_CRC
188    ("crc", Stable, &[]),
189    // FEAT_CSSC
190    ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]),
191    // FEAT_DIT
192    ("dit", Stable, &[]),
193    // FEAT_DotProd
194    ("dotprod", Stable, &["neon"]),
195    // FEAT_DPB
196    ("dpb", Stable, &[]),
197    // FEAT_DPB2
198    ("dpb2", Stable, &["dpb"]),
199    // FEAT_ECV
200    ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]),
201    // FEAT_F32MM
202    ("f32mm", Stable, &["sve"]),
203    // FEAT_F64MM
204    ("f64mm", Stable, &["sve"]),
205    // FEAT_FAMINMAX
206    ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]),
207    // FEAT_FCMA
208    ("fcma", Stable, &["neon"]),
209    // FEAT_FHM
210    ("fhm", Stable, &["fp16"]),
211    // FEAT_FLAGM
212    ("flagm", Stable, &[]),
213    // FEAT_FLAGM2
214    ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]),
215    // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`.
216    ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]),
217    // FEAT_FP16
218    // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608
219    ("fp16", Stable, &["neon"]),
220    // FEAT_FP8
221    ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]),
222    // FEAT_FP8DOT2
223    ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]),
224    // FEAT_FP8DOT4
225    ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]),
226    // FEAT_FP8FMA
227    ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]),
228    // FEAT_FRINTTS
229    ("frintts", Stable, &[]),
230    // FEAT_HBC
231    ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]),
232    // FEAT_I8MM
233    ("i8mm", Stable, &[]),
234    // FEAT_JSCVT
235    // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608
236    ("jsconv", Stable, &["neon"]),
237    // FEAT_LOR
238    ("lor", Stable, &[]),
239    // FEAT_LSE
240    ("lse", Stable, &[]),
241    // FEAT_LSE128
242    ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]),
243    // FEAT_LSE2
244    ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]),
245    // FEAT_LUT
246    ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]),
247    // FEAT_MOPS
248    ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]),
249    // FEAT_MTE & FEAT_MTE2
250    ("mte", Stable, &[]),
251    // FEAT_AdvSimd & FEAT_FP
252    ("neon", Stable, &[]),
253    // FEAT_PAUTH (address authentication)
254    ("paca", Stable, &[]),
255    // FEAT_PAUTH (generic authentication)
256    ("pacg", Stable, &[]),
257    // FEAT_PAN
258    ("pan", Stable, &[]),
259    // FEAT_PAuth_LR
260    ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]),
261    // FEAT_PMUv3
262    ("pmuv3", Stable, &[]),
263    // FEAT_RNG
264    ("rand", Stable, &[]),
265    // FEAT_RAS & FEAT_RASv1p1
266    ("ras", Stable, &[]),
267    // FEAT_LRCPC
268    ("rcpc", Stable, &[]),
269    // FEAT_LRCPC2
270    ("rcpc2", Stable, &["rcpc"]),
271    // FEAT_LRCPC3
272    ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]),
273    // FEAT_RDM
274    ("rdm", Stable, &["neon"]),
275    // This is needed for inline assembly, but shouldn't be stabilized as-is
276    // since it should be enabled globally using -Zfixed-x18, not
277    // #[target_feature].
278    // Note that cfg(target_feature = "reserve-x18") is currently not set for
279    // targets that reserve x18 by default.
280    ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]),
281    // FEAT_SB
282    ("sb", Stable, &[]),
283    // FEAT_SHA1 & FEAT_SHA256
284    ("sha2", Stable, &["neon"]),
285    // FEAT_SHA512 & FEAT_SHA3
286    ("sha3", Stable, &["sha2"]),
287    // FEAT_SM3 & FEAT_SM4
288    ("sm4", Stable, &["neon"]),
289    // FEAT_SME
290    ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]),
291    // FEAT_SME_B16B16
292    ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]),
293    // FEAT_SME_F16F16
294    ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]),
295    // FEAT_SME_F64F64
296    ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]),
297    // FEAT_SME_F8F16
298    ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]),
299    // FEAT_SME_F8F32
300    ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]),
301    // FEAT_SME_FA64
302    ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]),
303    // FEAT_SME_I16I64
304    ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]),
305    // FEAT_SME_LUTv2
306    ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]),
307    // FEAT_SME2
308    ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]),
309    // FEAT_SME2p1
310    ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]),
311    // FEAT_SPE
312    ("spe", Stable, &[]),
313    // FEAT_SSBS & FEAT_SSBS2
314    ("ssbs", Stable, &[]),
315    // FEAT_SSVE_FP8FDOT2
316    ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]),
317    // FEAT_SSVE_FP8FDOT4
318    ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]),
319    // FEAT_SSVE_FP8FMA
320    ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]),
321    // FEAT_SVE
322    // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608
323    //
324    // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always
325    // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2
326    //
327    // "For backwards compatibility, Neon and VFP are required in the latest architectures."
328    ("sve", Stable, &["neon"]),
329    // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
330    ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]),
331    // FEAT_SVE2
332    ("sve2", Stable, &["sve"]),
333    // FEAT_SVE_AES & FEAT_SVE_PMULL128
334    ("sve2-aes", Stable, &["sve2", "aes"]),
335    // FEAT_SVE2_BitPerm
336    ("sve2-bitperm", Stable, &["sve2"]),
337    // FEAT_SVE2_SHA3
338    ("sve2-sha3", Stable, &["sve2", "sha3"]),
339    // FEAT_SVE2_SM4
340    ("sve2-sm4", Stable, &["sve2", "sm4"]),
341    // FEAT_SVE2p1
342    ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]),
343    // FEAT_TME
344    ("tme", Stable, &[]),
345    (
346        "v8.1a",
347        Unstable(sym::aarch64_ver_target_feature),
348        &["crc", "lse", "rdm", "pan", "lor", "vh"],
349    ),
350    ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]),
351    (
352        "v8.3a",
353        Unstable(sym::aarch64_ver_target_feature),
354        &["v8.2a", "rcpc", "paca", "pacg", "jsconv"],
355    ),
356    ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]),
357    ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]),
358    ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]),
359    ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]),
360    ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]),
361    ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]),
362    ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]),
363    ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]),
364    ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]),
365    ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]),
366    ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]),
367    ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]),
368    // FEAT_VHE
369    ("vh", Stable, &[]),
370    // FEAT_WFxT
371    ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]),
372    // tidy-alphabetical-end
373];
374
375const AARCH64_TIED_FEATURES: &[&[&str]] = &[
376    &["paca", "pacg"], // Together these represent `pauth` in LLVM
377];
378
379static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
380    // tidy-alphabetical-start
381    ("adx", Stable, &[]),
382    ("aes", Stable, &["sse2"]),
383    ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
384    ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
385    ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
386    ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
387    ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]),
388    ("avx", Stable, &["sse4.2"]),
389    ("avx2", Stable, &["avx"]),
390    ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
391    ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]),
392    ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]),
393    ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]),
394    ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]),
395    ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]),
396    ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]),
397    ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]),
398    ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]),
399    ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]),
400    ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]),
401    ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]),
402    ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]),
403    ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]),
404    ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]),
405    ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]),
406    ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]),
407    ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]),
408    ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]),
409    ("bmi1", Stable, &[]),
410    ("bmi2", Stable, &[]),
411    ("cmpxchg16b", Stable, &[]),
412    ("ermsb", Unstable(sym::ermsb_target_feature), &[]),
413    ("f16c", Stable, &["avx"]),
414    ("fma", Stable, &["avx"]),
415    ("fxsr", Stable, &[]),
416    ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]),
417    ("kl", Unstable(sym::keylocker_x86), &["sse2"]),
418    ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
419    ("lzcnt", Stable, &[]),
420    ("movbe", Stable, &[]),
421    ("pclmulqdq", Stable, &["sse2"]),
422    ("popcnt", Stable, &[]),
423    ("prfchw", Unstable(sym::prfchw_target_feature), &[]),
424    ("rdrand", Stable, &[]),
425    ("rdseed", Stable, &[]),
426    ("rtm", Unstable(sym::rtm_target_feature), &[]),
427    ("sha", Stable, &["sse2"]),
428    ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]),
429    ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]),
430    ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]),
431    // This cannot actually be toggled, the ABI always fixes it, so it'd make little sense to
432    // stabilize. It must be in this list for the ABI check to be able to use it.
433    ("soft-float", Stability::Unstable(sym::x87_target_feature), &[]),
434    ("sse", Stable, &[]),
435    ("sse2", Stable, &["sse"]),
436    ("sse3", Stable, &["sse2"]),
437    ("sse4.1", Stable, &["ssse3"]),
438    ("sse4.2", Stable, &["sse4.1"]),
439    ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]),
440    ("ssse3", Stable, &["sse3"]),
441    ("tbm", Unstable(sym::tbm_target_feature), &[]),
442    ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]),
443    ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]),
444    ("widekl", Unstable(sym::keylocker_x86), &["kl"]),
445    ("x87", Unstable(sym::x87_target_feature), &[]),
446    ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]),
447    ("xsave", Stable, &[]),
448    ("xsavec", Stable, &["xsave"]),
449    ("xsaveopt", Stable, &["xsave"]),
450    ("xsaves", Stable, &["xsave"]),
451    // tidy-alphabetical-end
452];
453
454const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
455    // tidy-alphabetical-start
456    ("hvx", Unstable(sym::hexagon_target_feature), &[]),
457    ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]),
458    // tidy-alphabetical-end
459];
460
461static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
462    // tidy-alphabetical-start
463    ("altivec", Unstable(sym::powerpc_target_feature), &[]),
464    ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]),
465    ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]),
466    ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]),
467    ("power8-crypto", Unstable(sym::powerpc_target_feature), &["power8-altivec"]),
468    ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]),
469    ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]),
470    ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]),
471    ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]),
472    ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]),
473    // tidy-alphabetical-end
474];
475
476const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
477    // tidy-alphabetical-start
478    ("fp64", Unstable(sym::mips_target_feature), &[]),
479    ("msa", Unstable(sym::mips_target_feature), &[]),
480    ("virt", Unstable(sym::mips_target_feature), &[]),
481    // tidy-alphabetical-end
482];
483
484static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
485    // tidy-alphabetical-start
486    ("a", Stable, &["zaamo", "zalrsc"]),
487    ("c", Stable, &[]),
488    ("d", Unstable(sym::riscv_target_feature), &["f"]),
489    ("e", Unstable(sym::riscv_target_feature), &[]),
490    ("f", Unstable(sym::riscv_target_feature), &[]),
491    (
492        "forced-atomics",
493        Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" },
494        &[],
495    ),
496    ("m", Stable, &[]),
497    ("relax", Unstable(sym::riscv_target_feature), &[]),
498    ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]),
499    ("v", Unstable(sym::riscv_target_feature), &[]),
500    ("zaamo", Unstable(sym::riscv_target_feature), &[]),
501    ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]),
502    ("zalrsc", Unstable(sym::riscv_target_feature), &[]),
503    ("zba", Stable, &[]),
504    ("zbb", Stable, &[]),
505    ("zbc", Stable, &[]),
506    ("zbkb", Stable, &[]),
507    ("zbkc", Stable, &[]),
508    ("zbkx", Stable, &[]),
509    ("zbs", Stable, &[]),
510    ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]),
511    ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]),
512    ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]),
513    ("zfinx", Unstable(sym::riscv_target_feature), &[]),
514    ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]),
515    ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]),
516    ("zk", Stable, &["zkn", "zkr", "zkt"]),
517    ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]),
518    ("zknd", Stable, &[]),
519    ("zkne", Stable, &[]),
520    ("zknh", Stable, &[]),
521    ("zkr", Stable, &[]),
522    ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]),
523    ("zksed", Stable, &[]),
524    ("zksh", Stable, &[]),
525    ("zkt", Stable, &[]),
526    // tidy-alphabetical-end
527];
528
529static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
530    // tidy-alphabetical-start
531    ("atomics", Unstable(sym::wasm_target_feature), &[]),
532    ("bulk-memory", Stable, &[]),
533    ("exception-handling", Unstable(sym::wasm_target_feature), &[]),
534    ("extended-const", Stable, &[]),
535    ("multivalue", Stable, &[]),
536    ("mutable-globals", Stable, &[]),
537    ("nontrapping-fptoint", Stable, &[]),
538    ("reference-types", Stable, &[]),
539    ("relaxed-simd", Stable, &["simd128"]),
540    ("sign-ext", Stable, &[]),
541    ("simd128", Stable, &[]),
542    ("tail-call", Stable, &[]),
543    ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]),
544    // tidy-alphabetical-end
545];
546
547const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] =
548    &[("alu32", Unstable(sym::bpf_target_feature), &[])];
549
550static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
551    // tidy-alphabetical-start
552    ("10e60", Unstable(sym::csky_target_feature), &["7e10"]),
553    ("2e3", Unstable(sym::csky_target_feature), &["e2"]),
554    ("3e3r1", Unstable(sym::csky_target_feature), &[]),
555    ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]),
556    ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]),
557    ("3e7", Unstable(sym::csky_target_feature), &["2e3"]),
558    ("7e10", Unstable(sym::csky_target_feature), &["3e7"]),
559    ("cache", Unstable(sym::csky_target_feature), &[]),
560    ("doloop", Unstable(sym::csky_target_feature), &[]),
561    ("dsp1e2", Unstable(sym::csky_target_feature), &[]),
562    ("dspe60", Unstable(sym::csky_target_feature), &[]),
563    ("e1", Unstable(sym::csky_target_feature), &["elrw"]),
564    ("e2", Unstable(sym::csky_target_feature), &["e2"]),
565    ("edsp", Unstable(sym::csky_target_feature), &[]),
566    ("elrw", Unstable(sym::csky_target_feature), &[]),
567    ("float1e2", Unstable(sym::csky_target_feature), &[]),
568    ("float1e3", Unstable(sym::csky_target_feature), &[]),
569    ("float3e4", Unstable(sym::csky_target_feature), &[]),
570    ("float7e60", Unstable(sym::csky_target_feature), &[]),
571    ("floate1", Unstable(sym::csky_target_feature), &[]),
572    ("hard-tp", Unstable(sym::csky_target_feature), &[]),
573    ("high-registers", Unstable(sym::csky_target_feature), &[]),
574    ("hwdiv", Unstable(sym::csky_target_feature), &[]),
575    ("mp", Unstable(sym::csky_target_feature), &["2e3"]),
576    ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]),
577    ("nvic", Unstable(sym::csky_target_feature), &[]),
578    ("trust", Unstable(sym::csky_target_feature), &[]),
579    ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]),
580    ("vdspv1", Unstable(sym::csky_target_feature), &[]),
581    ("vdspv2", Unstable(sym::csky_target_feature), &[]),
582    // tidy-alphabetical-end
583    //fpu
584    // tidy-alphabetical-start
585    ("fdivdu", Unstable(sym::csky_target_feature), &[]),
586    ("fpuv2_df", Unstable(sym::csky_target_feature), &[]),
587    ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]),
588    ("fpuv3_df", Unstable(sym::csky_target_feature), &[]),
589    ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]),
590    ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]),
591    ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]),
592    ("hard-float", Unstable(sym::csky_target_feature), &[]),
593    ("hard-float-abi", Unstable(sym::csky_target_feature), &[]),
594    // tidy-alphabetical-end
595];
596
597static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
598    // tidy-alphabetical-start
599    ("d", Unstable(sym::loongarch_target_feature), &["f"]),
600    ("f", Unstable(sym::loongarch_target_feature), &[]),
601    ("frecipe", Unstable(sym::loongarch_target_feature), &[]),
602    ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]),
603    ("lbt", Unstable(sym::loongarch_target_feature), &[]),
604    ("lsx", Unstable(sym::loongarch_target_feature), &["d"]),
605    ("lvz", Unstable(sym::loongarch_target_feature), &[]),
606    ("relax", Unstable(sym::loongarch_target_feature), &[]),
607    ("ual", Unstable(sym::loongarch_target_feature), &[]),
608    // tidy-alphabetical-end
609];
610
611const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
612    // tidy-alphabetical-start
613    ("backchain", Unstable(sym::s390x_target_feature), &[]),
614    ("vector", Unstable(sym::s390x_target_feature), &[]),
615    // tidy-alphabetical-end
616];
617
618const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
619    // tidy-alphabetical-start
620    ("leoncasa", Unstable(sym::sparc_target_feature), &[]),
621    ("v8plus", Unstable(sym::sparc_target_feature), &[]),
622    ("v9", Unstable(sym::sparc_target_feature), &[]),
623    // tidy-alphabetical-end
624];
625
626static M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
627    // tidy-alphabetical-start
628    ("isa-68000", Unstable(sym::m68k_target_feature), &[]),
629    ("isa-68010", Unstable(sym::m68k_target_feature), &["isa-68000"]),
630    ("isa-68020", Unstable(sym::m68k_target_feature), &["isa-68010"]),
631    ("isa-68030", Unstable(sym::m68k_target_feature), &["isa-68020"]),
632    ("isa-68040", Unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]),
633    ("isa-68060", Unstable(sym::m68k_target_feature), &["isa-68040"]),
634    // FPU
635    ("isa-68881", Unstable(sym::m68k_target_feature), &[]),
636    ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]),
637    // tidy-alphabetical-end
638];
639
640/// When rustdoc is running, provide a list of all known features so that all their respective
641/// primitives may be documented.
642///
643/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
644pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> {
645    std::iter::empty()
646        .chain(ARM_FEATURES.iter())
647        .chain(AARCH64_FEATURES.iter())
648        .chain(X86_FEATURES.iter())
649        .chain(HEXAGON_FEATURES.iter())
650        .chain(POWERPC_FEATURES.iter())
651        .chain(MIPS_FEATURES.iter())
652        .chain(RISCV_FEATURES.iter())
653        .chain(WASM_FEATURES.iter())
654        .chain(BPF_FEATURES.iter())
655        .chain(CSKY_FEATURES)
656        .chain(LOONGARCH_FEATURES)
657        .chain(IBMZ_FEATURES)
658        .chain(SPARC_FEATURES)
659        .chain(M68K_FEATURES)
660        .cloned()
661        .map(|(f, s, _)| (f, s))
662}
663
664// These arrays represent the least-constraining feature that is required for vector types up to a
665// certain size to have their "proper" ABI on each architecture.
666// Note that they must be kept sorted by vector size.
667const X86_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
668    &[(128, "sse"), (256, "avx"), (512, "avx512f")]; // FIXME: might need changes for AVX10.
669const AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")];
670
671// We might want to add "helium" too.
672const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")];
673
674const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")];
675const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")];
676const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")];
677const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
678    &[/*(64, "zvl64b"), */ (128, "v")];
679// Always warn on SPARC, as the necessary target features cannot be enabled in Rust at the moment.
680const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/];
681
682const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
683    &[/*(512, "hvx-length64b"),*/ (1024, "hvx-length128b")];
684const MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")];
685const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vdspv1")];
686const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
687    &[(128, "lsx"), (256, "lasx")];
688
689#[derive(Copy, Clone, Debug)]
690pub struct FeatureConstraints {
691    /// Features that must be enabled.
692    pub required: &'static [&'static str],
693    /// Features that must be disabled.
694    pub incompatible: &'static [&'static str],
695}
696
697impl Target {
698    pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] {
699        match &*self.arch {
700            "arm" => ARM_FEATURES,
701            "aarch64" | "arm64ec" => AARCH64_FEATURES,
702            "x86" | "x86_64" => X86_FEATURES,
703            "hexagon" => HEXAGON_FEATURES,
704            "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES,
705            "powerpc" | "powerpc64" => POWERPC_FEATURES,
706            "riscv32" | "riscv64" => RISCV_FEATURES,
707            "wasm32" | "wasm64" => WASM_FEATURES,
708            "bpf" => BPF_FEATURES,
709            "csky" => CSKY_FEATURES,
710            "loongarch64" => LOONGARCH_FEATURES,
711            "s390x" => IBMZ_FEATURES,
712            "sparc" | "sparc64" => SPARC_FEATURES,
713            "m68k" => M68K_FEATURES,
714            _ => &[],
715        }
716    }
717
718    pub fn features_for_correct_vector_abi(&self) -> &'static [(u64, &'static str)] {
719        match &*self.arch {
720            "x86" | "x86_64" => X86_FEATURES_FOR_CORRECT_VECTOR_ABI,
721            "aarch64" | "arm64ec" => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI,
722            "arm" => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI,
723            "powerpc" | "powerpc64" => POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI,
724            "loongarch64" => LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI,
725            "riscv32" | "riscv64" => RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI,
726            "wasm32" | "wasm64" => WASM_FEATURES_FOR_CORRECT_VECTOR_ABI,
727            "s390x" => S390X_FEATURES_FOR_CORRECT_VECTOR_ABI,
728            "sparc" | "sparc64" => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI,
729            "hexagon" => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI,
730            "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI,
731            "bpf" | "m68k" => &[], // no vector ABI
732            "csky" => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI,
733            // FIXME: for some tier3 targets, we are overly cautious and always give warnings
734            // when passing args in vector registers.
735            _ => &[],
736        }
737    }
738
739    pub fn tied_target_features(&self) -> &'static [&'static [&'static str]] {
740        match &*self.arch {
741            "aarch64" | "arm64ec" => AARCH64_TIED_FEATURES,
742            _ => &[],
743        }
744    }
745
746    pub fn implied_target_features<'a>(
747        &self,
748        base_features: impl Iterator<Item = &'a str>,
749    ) -> FxHashSet<&'a str> {
750        let implied_features =
751            self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::<FxHashMap<_, _>>();
752
753        // implied target features have their own implied target features, so we traverse the
754        // map until there are no more features to add
755        let mut features = FxHashSet::default();
756        let mut new_features = base_features.collect::<Vec<&str>>();
757        while let Some(new_feature) = new_features.pop() {
758            if features.insert(new_feature) {
759                if let Some(implied_features) = implied_features.get(&new_feature) {
760                    new_features.extend(implied_features.iter().copied())
761                }
762            }
763        }
764        features
765    }
766
767    /// Returns two lists of features:
768    /// the first list contains target features that must be enabled for ABI reasons,
769    /// and the second list contains target feature that must be disabled for ABI reasons.
770    ///
771    /// These features are automatically appended to whatever the target spec sats as default
772    /// features for the target.
773    ///
774    /// All features enabled/disabled via `-Ctarget-features` and `#[target_features]` are checked
775    /// against this. We also check any implied features, based on the information above. If LLVM
776    /// implicitly enables more implied features than we do, that could bypass this check!
777    pub fn abi_required_features(&self) -> FeatureConstraints {
778        const NOTHING: FeatureConstraints = FeatureConstraints { required: &[], incompatible: &[] };
779        // Some architectures don't have a clean explicit ABI designation; instead, the ABI is
780        // defined by target features. When that is the case, those target features must be
781        // "forbidden" in the list above to ensure that there is a consistent answer to the
782        // questions "which ABI is used".
783        match &*self.arch {
784            "x86" => {
785                // We use our own ABI indicator here; LLVM does not have anything native.
786                // Every case should require or forbid `soft-float`!
787                match self.rustc_abi {
788                    None => {
789                        // Default hardfloat ABI.
790                        // x87 must be enabled, soft-float must be disabled.
791                        FeatureConstraints { required: &["x87"], incompatible: &["soft-float"] }
792                    }
793                    Some(RustcAbi::X86Sse2) => {
794                        // Extended hardfloat ABI. x87 and SSE2 must be enabled, soft-float must be disabled.
795                        FeatureConstraints {
796                            required: &["x87", "sse2"],
797                            incompatible: &["soft-float"],
798                        }
799                    }
800                    Some(RustcAbi::X86Softfloat) => {
801                        // Softfloat ABI, requires corresponding target feature. That feature trumps
802                        // `x87` and all other FPU features so those do not matter.
803                        // Note that this one requirement is the entire implementation of the ABI!
804                        // LLVM handles the rest.
805                        FeatureConstraints { required: &["soft-float"], incompatible: &[] }
806                    }
807                }
808            }
809            "x86_64" => {
810                // We use our own ABI indicator here; LLVM does not have anything native.
811                // Every case should require or forbid `soft-float`!
812                match self.rustc_abi {
813                    None => {
814                        // Default hardfloat ABI. On x86-64, this always includes SSE2.
815                        FeatureConstraints {
816                            required: &["x87", "sse2"],
817                            incompatible: &["soft-float"],
818                        }
819                    }
820                    Some(RustcAbi::X86Softfloat) => {
821                        // Softfloat ABI, requires corresponding target feature. That feature trumps
822                        // `x87` and all other FPU features so those do not matter.
823                        // Note that this one requirement is the entire implementation of the ABI!
824                        // LLVM handles the rest.
825                        FeatureConstraints { required: &["soft-float"], incompatible: &[] }
826                    }
827                    Some(r) => panic!("invalid Rust ABI for x86_64: {r:?}"),
828                }
829            }
830            "arm" => {
831                // On ARM, ABI handling is reasonably sane; we use `llvm_floatabi` to indicate
832                // to LLVM which ABI we are going for.
833                match self.llvm_floatabi.unwrap() {
834                    FloatAbi::Soft => {
835                        // Nothing special required, will use soft-float ABI throughout.
836                        // We can even allow `-soft-float` here; in fact that is useful as it lets
837                        // people use FPU instructions with a softfloat ABI (corresponds to
838                        // `-mfloat-abi=softfp` in GCC/clang).
839                        NOTHING
840                    }
841                    FloatAbi::Hard => {
842                        // Must have `fpregs` and must not have `soft-float`.
843                        FeatureConstraints { required: &["fpregs"], incompatible: &["soft-float"] }
844                    }
845                }
846            }
847            "aarch64" | "arm64ec" => {
848                // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force
849                // the use of soft-float, so all we can do here is some crude hacks.
850                match &*self.abi {
851                    "softfloat" => {
852                        // This is not fully correct, LLVM actually doesn't let us enforce the softfloat
853                        // ABI properly... see <https://github.com/rust-lang/rust/issues/134375>.
854                        // FIXME: should we forbid "neon" here? But that would be a breaking change.
855                        NOTHING
856                    }
857                    _ => {
858                        // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled.
859                        // These are Rust feature names and we use "neon" to control both of them.
860                        FeatureConstraints { required: &["neon"], incompatible: &[] }
861                    }
862                }
863            }
864            "riscv32" | "riscv64" => {
865                // RISC-V handles ABI in a very sane way, being fully explicit via `llvm_abiname`
866                // about what the intended ABI is.
867                match &*self.llvm_abiname {
868                    "ilp32d" | "lp64d" => {
869                        // Requires d (which implies f), incompatible with e.
870                        FeatureConstraints { required: &["d"], incompatible: &["e"] }
871                    }
872                    "ilp32f" | "lp64f" => {
873                        // Requires f, incompatible with e.
874                        FeatureConstraints { required: &["f"], incompatible: &["e"] }
875                    }
876                    "ilp32" | "lp64" => {
877                        // Requires nothing, incompatible with e.
878                        FeatureConstraints { required: &[], incompatible: &["e"] }
879                    }
880                    "ilp32e" => {
881                        // ilp32e is documented to be incompatible with features that need aligned
882                        // load/stores > 32 bits, like `d`. (One could also just generate more
883                        // complicated code to align the stack when needed, but the RISCV
884                        // architecture manual just explicitly rules out this combination so we
885                        // might as well.)
886                        // Note that the `e` feature is not required: the ABI treats the extra
887                        // registers as caller-save, so it is safe to use them only in some parts of
888                        // a program while the rest doesn't know they even exist.
889                        FeatureConstraints { required: &[], incompatible: &["d"] }
890                    }
891                    "lp64e" => {
892                        // As above, `e` is not required.
893                        NOTHING
894                    }
895                    _ => unreachable!(),
896                }
897            }
898            _ => NOTHING,
899        }
900    }
901}