Skip to content

Commit 45ebc40

Browse files
authored
Rollup merge of #137447 - folkertdev:simd-extract-insert-dyn, r=scottmcm
add `core::intrinsics::simd::{simd_extract_dyn, simd_insert_dyn}` fixes #137372 adds `core::intrinsics::simd::{simd_extract_dyn, simd_insert_dyn}`, which contrary to their non-dyn counterparts allow a non-const index. Many platforms (but notably not x86_64 or aarch64) have dedicated instructions for this operation, which stdarch can emit with this change. Future work is to also make the `Index` operation on the `Simd` type emit this operation, but the intrinsic can't be used directly. We'll need some MIR shenanigans for that. r? `@ghost`
2 parents e62d47d + 59c5533 commit 45ebc40

File tree

6 files changed

+197
-37
lines changed

6 files changed

+197
-37
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

+39-30
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
14211421
return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices));
14221422
}
14231423

1424-
if name == sym::simd_insert {
1424+
if name == sym::simd_insert || name == sym::simd_insert_dyn {
14251425
require!(
14261426
in_elem == arg_tys[2],
14271427
InvalidMonomorphization::InsertedType {
@@ -1432,40 +1432,49 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
14321432
out_ty: arg_tys[2]
14331433
}
14341434
);
1435-
let idx = bx
1436-
.const_to_opt_u128(args[1].immediate(), false)
1437-
.expect("typeck should have ensure that this is a const");
1438-
if idx >= in_len.into() {
1439-
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1440-
span,
1441-
name,
1442-
arg_idx: 1,
1443-
total_len: in_len.into(),
1444-
});
1445-
}
1446-
return Ok(bx.insert_element(
1447-
args[0].immediate(),
1448-
args[2].immediate(),
1449-
bx.const_i32(idx as i32),
1450-
));
1435+
1436+
let index_imm = if name == sym::simd_insert {
1437+
let idx = bx
1438+
.const_to_opt_u128(args[1].immediate(), false)
1439+
.expect("typeck should have ensure that this is a const");
1440+
if idx >= in_len.into() {
1441+
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1442+
span,
1443+
name,
1444+
arg_idx: 1,
1445+
total_len: in_len.into(),
1446+
});
1447+
}
1448+
bx.const_i32(idx as i32)
1449+
} else {
1450+
args[1].immediate()
1451+
};
1452+
1453+
return Ok(bx.insert_element(args[0].immediate(), args[2].immediate(), index_imm));
14511454
}
1452-
if name == sym::simd_extract {
1455+
if name == sym::simd_extract || name == sym::simd_extract_dyn {
14531456
require!(
14541457
ret_ty == in_elem,
14551458
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
14561459
);
1457-
let idx = bx
1458-
.const_to_opt_u128(args[1].immediate(), false)
1459-
.expect("typeck should have ensure that this is a const");
1460-
if idx >= in_len.into() {
1461-
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1462-
span,
1463-
name,
1464-
arg_idx: 1,
1465-
total_len: in_len.into(),
1466-
});
1467-
}
1468-
return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32)));
1460+
let index_imm = if name == sym::simd_extract {
1461+
let idx = bx
1462+
.const_to_opt_u128(args[1].immediate(), false)
1463+
.expect("typeck should have ensure that this is a const");
1464+
if idx >= in_len.into() {
1465+
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
1466+
span,
1467+
name,
1468+
arg_idx: 1,
1469+
total_len: in_len.into(),
1470+
});
1471+
}
1472+
bx.const_i32(idx as i32)
1473+
} else {
1474+
args[1].immediate()
1475+
};
1476+
1477+
return Ok(bx.extract_element(args[0].immediate(), index_imm));
14691478
}
14701479

14711480
if name == sym::simd_select {

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -659,8 +659,12 @@ pub(crate) fn check_intrinsic_type(
659659
sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)),
660660
sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit),
661661
sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit),
662-
sym::simd_insert => (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)),
663-
sym::simd_extract => (2, 0, vec![param(0), tcx.types.u32], param(1)),
662+
sym::simd_insert | sym::simd_insert_dyn => {
663+
(2, 0, vec![param(0), tcx.types.u32, param(1)], param(0))
664+
}
665+
sym::simd_extract | sym::simd_extract_dyn => {
666+
(2, 0, vec![param(0), tcx.types.u32], param(1))
667+
}
664668
sym::simd_cast
665669
| sym::simd_as
666670
| sym::simd_cast_ptr

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,7 @@ symbols! {
19131913
simd_eq,
19141914
simd_expose_provenance,
19151915
simd_extract,
1916+
simd_extract_dyn,
19161917
simd_fabs,
19171918
simd_fcos,
19181919
simd_fexp,
@@ -1931,6 +1932,7 @@ symbols! {
19311932
simd_ge,
19321933
simd_gt,
19331934
simd_insert,
1935+
simd_insert_dyn,
19341936
simd_le,
19351937
simd_lt,
19361938
simd_masked_load,

library/core/src/intrinsics/simd.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
/// Inserts an element into a vector, returning the updated vector.
66
///
7-
/// `T` must be a vector with element type `U`.
7+
/// `T` must be a vector with element type `U`, and `idx` must be `const`.
88
///
99
/// # Safety
1010
///
@@ -15,15 +15,48 @@ pub const unsafe fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
1515

1616
/// Extracts an element from a vector.
1717
///
18-
/// `T` must be a vector with element type `U`.
18+
/// `T` must be a vector with element type `U`, and `idx` must be `const`.
1919
///
2020
/// # Safety
2121
///
22-
/// `idx` must be in-bounds of the vector.
22+
/// `idx` must be const and in-bounds of the vector.
2323
#[rustc_intrinsic]
2424
#[rustc_nounwind]
2525
pub const unsafe fn simd_extract<T, U>(x: T, idx: u32) -> U;
2626

27+
/// Inserts an element into a vector, returning the updated vector.
28+
///
29+
/// `T` must be a vector with element type `U`.
30+
///
31+
/// If the index is `const`, [`simd_insert`] may emit better assembly.
32+
///
33+
/// # Safety
34+
///
35+
/// `idx` must be in-bounds of the vector.
36+
#[rustc_nounwind]
37+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
38+
pub unsafe fn simd_insert_dyn<T, U>(mut x: T, idx: u32, val: U) -> T {
39+
// SAFETY: `idx` must be in-bounds
40+
unsafe { (&raw mut x).cast::<U>().add(idx as usize).write(val) }
41+
x
42+
}
43+
44+
/// Extracts an element from a vector.
45+
///
46+
/// `T` must be a vector with element type `U`.
47+
///
48+
/// If the index is `const`, [`simd_extract`] may emit better assembly.
49+
///
50+
/// # Safety
51+
///
52+
/// `idx` must be in-bounds of the vector.
53+
#[rustc_nounwind]
54+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
55+
pub unsafe fn simd_extract_dyn<T, U>(x: T, idx: u32) -> U {
56+
// SAFETY: `idx` must be in-bounds
57+
unsafe { (&raw const x).cast::<U>().add(idx as usize).read() }
58+
}
59+
2760
/// Adds two simd vectors elementwise.
2861
///
2962
/// `T` must be a vector of integers or floats.
+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//@compile-flags: -C opt-level=3 -C no-prepopulate-passes
2+
3+
#![feature(core_intrinsics, repr_simd)]
4+
#![no_std]
5+
#![crate_type = "lib"]
6+
#![allow(non_camel_case_types)]
7+
8+
// Test that `core::intrinsics::simd::{simd_extract_dyn, simd_insert_dyn}`
9+
// lower to an LLVM extractelement or insertelement operation.
10+
11+
use core::intrinsics::simd::{simd_extract, simd_extract_dyn, simd_insert, simd_insert_dyn};
12+
13+
#[repr(simd)]
14+
#[derive(Clone, Copy)]
15+
pub struct u32x16([u32; 16]);
16+
17+
#[repr(simd)]
18+
#[derive(Clone, Copy)]
19+
pub struct i8x16([i8; 16]);
20+
21+
// CHECK-LABEL: dyn_simd_extract
22+
// CHECK: extractelement <16 x i8> %x, i32 %idx
23+
#[no_mangle]
24+
unsafe extern "C" fn dyn_simd_extract(x: i8x16, idx: u32) -> i8 {
25+
simd_extract_dyn(x, idx)
26+
}
27+
28+
// CHECK-LABEL: literal_dyn_simd_extract
29+
// CHECK: extractelement <16 x i8> %x, i32 7
30+
#[no_mangle]
31+
unsafe extern "C" fn literal_dyn_simd_extract(x: i8x16) -> i8 {
32+
simd_extract_dyn(x, 7)
33+
}
34+
35+
// CHECK-LABEL: const_dyn_simd_extract
36+
// CHECK: extractelement <16 x i8> %x, i32 7
37+
#[no_mangle]
38+
unsafe extern "C" fn const_dyn_simd_extract(x: i8x16) -> i8 {
39+
simd_extract_dyn(x, const { 3 + 4 })
40+
}
41+
42+
// CHECK-LABEL: const_simd_extract
43+
// CHECK: extractelement <16 x i8> %x, i32 7
44+
#[no_mangle]
45+
unsafe extern "C" fn const_simd_extract(x: i8x16) -> i8 {
46+
simd_extract(x, const { 3 + 4 })
47+
}
48+
49+
// CHECK-LABEL: dyn_simd_insert
50+
// CHECK: insertelement <16 x i8> %x, i8 %e, i32 %idx
51+
#[no_mangle]
52+
unsafe extern "C" fn dyn_simd_insert(x: i8x16, e: i8, idx: u32) -> i8x16 {
53+
simd_insert_dyn(x, idx, e)
54+
}
55+
56+
// CHECK-LABEL: literal_dyn_simd_insert
57+
// CHECK: insertelement <16 x i8> %x, i8 %e, i32 7
58+
#[no_mangle]
59+
unsafe extern "C" fn literal_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 {
60+
simd_insert_dyn(x, 7, e)
61+
}
62+
63+
// CHECK-LABEL: const_dyn_simd_insert
64+
// CHECK: insertelement <16 x i8> %x, i8 %e, i32 7
65+
#[no_mangle]
66+
unsafe extern "C" fn const_dyn_simd_insert(x: i8x16, e: i8) -> i8x16 {
67+
simd_insert_dyn(x, const { 3 + 4 }, e)
68+
}
69+
70+
// CHECK-LABEL: const_simd_insert
71+
// CHECK: insertelement <16 x i8> %x, i8 %e, i32 7
72+
#[no_mangle]
73+
unsafe extern "C" fn const_simd_insert(x: i8x16, e: i8) -> i8x16 {
74+
simd_insert(x, const { 3 + 4 }, e)
75+
}

tests/ui/simd/intrinsic/generic-elements-pass.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//@ run-pass
22

3-
#![feature(repr_simd, core_intrinsics)]
3+
#![feature(repr_simd, intrinsics, core_intrinsics)]
44

5-
use std::intrinsics::simd::{simd_extract, simd_insert, simd_shuffle};
5+
use std::intrinsics::simd::{
6+
simd_extract, simd_extract_dyn, simd_insert, simd_insert_dyn, simd_shuffle,
7+
};
68

79
#[repr(simd)]
810
#[derive(Copy, Clone, Debug, PartialEq)]
@@ -70,6 +72,41 @@ fn main() {
7072
all_eq!(simd_extract(x8, 6), 86);
7173
all_eq!(simd_extract(x8, 7), 87);
7274
}
75+
unsafe {
76+
all_eq!(simd_insert_dyn(x2, 0, 100), i32x2([100, 21]));
77+
all_eq!(simd_insert_dyn(x2, 1, 100), i32x2([20, 100]));
78+
79+
all_eq!(simd_insert_dyn(x4, 0, 100), i32x4([100, 41, 42, 43]));
80+
all_eq!(simd_insert_dyn(x4, 1, 100), i32x4([40, 100, 42, 43]));
81+
all_eq!(simd_insert_dyn(x4, 2, 100), i32x4([40, 41, 100, 43]));
82+
all_eq!(simd_insert_dyn(x4, 3, 100), i32x4([40, 41, 42, 100]));
83+
84+
all_eq!(simd_insert_dyn(x8, 0, 100), i32x8([100, 81, 82, 83, 84, 85, 86, 87]));
85+
all_eq!(simd_insert_dyn(x8, 1, 100), i32x8([80, 100, 82, 83, 84, 85, 86, 87]));
86+
all_eq!(simd_insert_dyn(x8, 2, 100), i32x8([80, 81, 100, 83, 84, 85, 86, 87]));
87+
all_eq!(simd_insert_dyn(x8, 3, 100), i32x8([80, 81, 82, 100, 84, 85, 86, 87]));
88+
all_eq!(simd_insert_dyn(x8, 4, 100), i32x8([80, 81, 82, 83, 100, 85, 86, 87]));
89+
all_eq!(simd_insert_dyn(x8, 5, 100), i32x8([80, 81, 82, 83, 84, 100, 86, 87]));
90+
all_eq!(simd_insert_dyn(x8, 6, 100), i32x8([80, 81, 82, 83, 84, 85, 100, 87]));
91+
all_eq!(simd_insert_dyn(x8, 7, 100), i32x8([80, 81, 82, 83, 84, 85, 86, 100]));
92+
93+
all_eq!(simd_extract_dyn(x2, 0), 20);
94+
all_eq!(simd_extract_dyn(x2, 1), 21);
95+
96+
all_eq!(simd_extract_dyn(x4, 0), 40);
97+
all_eq!(simd_extract_dyn(x4, 1), 41);
98+
all_eq!(simd_extract_dyn(x4, 2), 42);
99+
all_eq!(simd_extract_dyn(x4, 3), 43);
100+
101+
all_eq!(simd_extract_dyn(x8, 0), 80);
102+
all_eq!(simd_extract_dyn(x8, 1), 81);
103+
all_eq!(simd_extract_dyn(x8, 2), 82);
104+
all_eq!(simd_extract_dyn(x8, 3), 83);
105+
all_eq!(simd_extract_dyn(x8, 4), 84);
106+
all_eq!(simd_extract_dyn(x8, 5), 85);
107+
all_eq!(simd_extract_dyn(x8, 6), 86);
108+
all_eq!(simd_extract_dyn(x8, 7), 87);
109+
}
73110

74111
let y2 = i32x2([120, 121]);
75112
let y4 = i32x4([140, 141, 142, 143]);

0 commit comments

Comments
 (0)