Skip to main content

proof_token/
proof-token.rs

1// This example demonstrates using `TargetFeatures` to create a "proof token type", a token that
2// demonstrates that particular target features have already been detected, and that those features
3// can be used safely.
4
5#![allow(unused, unused_macros, unused_imports)]
6
7use target_features::TargetFeatures;
8
9/// Make sure proof tokens can't be improperly constructed
10mod unconstructible {
11    pub struct Unconstructible(());
12    impl Unconstructible {
13        pub unsafe fn new() -> Self {
14            Self(())
15        }
16    }
17}
18use unconstructible::Unconstructible;
19
20/// Proof of target feature support.
21///
22/// # Safety
23/// The type must be implemented such that it's impossible to safely construct without ensuring the
24/// specified target features are supported.
25unsafe trait Proof: Sized {
26    /// The proven target features
27    const TARGET: TargetFeatures;
28
29    /// Detect the support for the target features
30    fn detect() -> Option<Self>;
31
32    /// Assume the target features are supported
33    ///
34    /// # Safety
35    /// Calling this is undefined if the target features are not supported
36    unsafe fn assume() -> Self;
37}
38
39/// Make a proof token type for a particular set of features
40macro_rules! make_target_proof {
41    { $vis:vis struct $proof:ident($($feature:tt),*); } => {
42        $vis struct $proof(Unconstructible);
43
44        unsafe impl Proof for $proof {
45            // Build on the already-known target features
46            const TARGET: TargetFeatures =
47                TargetFeatures::enabled_for_target()
48                    .with(target_features::target_features!($($feature),*));
49
50            fn detect() -> Option<Self> {
51                if true $(&& is_x86_feature_detected!($feature))* {
52                    unsafe { Some(Self::assume()) }
53                } else {
54                    None
55                }
56            }
57
58            unsafe fn assume() -> Self {
59                Self(Unconstructible::new())
60            }
61        }
62    }
63}
64
65/// A function that can only be called with the "avx" feature, or panics otherwise.
66#[cfg(target_arch = "x86_64")]
67fn safe_avx_fn<P: Proof>(_: P) {
68    #[target_feature(enable = "avx")]
69    unsafe fn unsafe_avx_fn() {
70        println!("called an avx function")
71    }
72
73    // Future improvements to const generics might make it possible to assert this at compile time.
74    // Since P::TARGET is const, this assert disappears if the required features are present.
75    assert!(
76        P::TARGET.contains(target_features::x86_64::AVX),
77        "avx feature not supported"
78    );
79    unsafe { unsafe_avx_fn() }
80}
81
82#[cfg(target_arch = "x86_64")]
83fn main() {
84    // The function can be called with the exact features
85    make_target_proof! {
86        struct Avx("avx");
87    }
88    if let Some(proof) = Avx::detect() {
89        safe_avx_fn(proof);
90    }
91
92    // The function can also be called with a target that implies the required features
93    make_target_proof! {
94        struct Avx2("avx2");
95    }
96    if let Some(proof) = Avx2::detect() {
97        safe_avx_fn(proof);
98    }
99
100    // This panics, unless compiled with something like `-Ctarget-feature=+avx`
101    make_target_proof! {
102        struct Aes("aes");
103    }
104    if let Some(proof) = Aes::detect() {
105        safe_avx_fn(proof);
106    }
107}
108
109#[cfg(not(target_arch = "x86_64"))]
110fn main() {}