Use const generics for stdarch intrinsics · Issue #83167 · rust-lang/rust (original) (raw)

Background

rust-lang/stdarch#248

core::arch contains architecture-specific vendor intrinsics, some of which have arguments that are required to be compile-time constants. This is tricky because LLVM will often assert/ICE if a constant is not passed into the LLVM intrinsic at the LLVM IR level. This was previously handled by using a match statement to manually monomorphize the intrinsic:

#[inline] #[target_feature(enable = "sse")] #[cfg_attr(test, assert_instr(shufps, mask = 3))] #[rustc_args_required_const(2)] #[stable(feature = "simd_x86", since = "1.27.0")] pub unsafe fn _mm_shuffle_ps(a: __m128, b: __m128, mask: i32) -> __m128 { let mask = (mask & 0xFF) as u8;

macro_rules! shuffle_done {
    ($x01:expr, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo>:</mo><mi>e</mi><mi>x</mi><mi>p</mi><mi>r</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23:expr, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mpunct">,</span></span></span></span>x45:expr, $x67:expr) => {
        simd_shuffle4(a, b, [$x01, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mpunct">,</span></span></span></span>x45, $x67])
    };
}
macro_rules! shuffle_x67 {
    ($x01:expr, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo>:</mo><mi>e</mi><mi>x</mi><mi>p</mi><mi>r</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23:expr, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mpunct">,</span></span></span></span>x45:expr) => {
        match (mask >> 6) & 0b11 {
            0b00 => shuffle_done!($x01, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mpunct">,</span></span></span></span>x45, 4),
            0b01 => shuffle_done!($x01, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mpunct">,</span></span></span></span>x45, 5),
            0b10 => shuffle_done!($x01, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mpunct">,</span></span></span></span>x45, 6),
            _ => shuffle_done!($x01, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mn>23</mn><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">x23, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8389em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mord">23</span><span class="mpunct">,</span></span></span></span>x45, 7),
        }
    };
}
macro_rules! shuffle_x45 {
    ($x01:expr, $x23:expr) => {
        match (mask >> 4) & 0b11 {
            0b00 => shuffle_x67!($x01, $x23, 4),
            0b01 => shuffle_x67!($x01, $x23, 5),
            0b10 => shuffle_x67!($x01, $x23, 6),
            _ => shuffle_x67!($x01, $x23, 7),
        }
    };
}
macro_rules! shuffle_x23 {
    ($x01:expr) => {
        match (mask >> 2) & 0b11 {
            0b00 => shuffle_x45!($x01, 0),
            0b01 => shuffle_x45!($x01, 1),
            0b10 => shuffle_x45!($x01, 2),
            _ => shuffle_x45!($x01, 3),
        }
    };
}
match mask & 0b11 {
    0b00 => shuffle_x23!(0),
    0b01 => shuffle_x23!(1),
    0b10 => shuffle_x23!(2),
    _ => shuffle_x23!(3),
}

}

The downside of this approach is that it generates huge MIR and severely bloats the size of libcore to the point where it actually affects compilation time.

The #[rustc_args_required_const] attribute was used to restrict arguments to constants so that we could eventually replace this with const generics. Note that it effectively only acts as a hard lint, the match is still needed to manually monomorphize the LLVM intrinsic.

New const generics support

rust-lang/stdarch#1022

All the intrinsics in stdarch have been converted to use the newly stabilized const generics:

#[inline] #[target_feature(enable = "sse")] #[cfg_attr(test, assert_instr(shufps, mask = 3))] #[rustc_legacy_const_generics(2)] #[stable(feature = "simd_x86", since = "1.27.0")] pub unsafe fn _mm_shuffle_ps(a: __m128, b: __m128) -> __m128 { static_assert!(mask: i32 where mask >= 0 && mask <= 255); simd_shuffle4( a, b, [ mask as u32 & 0b11, (mask as u32 >> 2) & 0b11, ((mask as u32 >> 4) & 0b11) + 4, ((mask as u32 >> 6) & 0b11) + 4, ], ) }

To preserve backwards compatibility with the already stabilized intrinsics using #[rustc_args_required_const], a new attribute #[rustc_legacy_const_generics] was added in #82447 which rewrites function calls of the form func(a, b, c) to func::<{b}>(a, c).

This new attribute is not intended to ever be stabilized, it is only intended for use in stdarch as a replacement for #[rustc_args_required_const].

#[rustc_legacy_const_generics(1)] pub fn foo(x: usize, z: usize) -> [usize; 3] { [x, Y, z] }

fn main() { assert_eq!(foo(0 + 0, 1 + 1, 2 + 2), [0, 2, 4]); assert_eq!(foo::<{1 + 1}>(0 + 0, 2 + 2), [0, 2, 4]); }

Open questions/issues