Skip to content

Commit 095b44c

Browse files
committed
Auto merge of #57269 - gnzlbg:simd_bitmask, r=rkruppe
Add intrinsic to create an integer bitmask from a vector mask This PR adds a new simd intrinsic: `simd_bitmask(vector) -> unsigned integer` that creates an integer bitmask from a vector mask by extracting one bit of each vector lane. This is required to implement: rust-lang/packed_simd#166 . EDIT: the reason we need an intrinsics for this is that we have to truncate the vector lanes to an `<i1 x N>` vector, and then bitcast that to an `iN` integer (while making sure that we only materialize `i8`, ... , `i64` - that is, no `i1`, `i2`, `i4`, types), and we can't do any of that in a Rust library. r? @rkruppe
2 parents f23b63c + 785f529 commit 095b44c

File tree

6 files changed

+288
-0
lines changed

6 files changed

+288
-0
lines changed

src/librustc_codegen_llvm/intrinsic.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,52 @@ fn generic_simd_intrinsic(
11671167
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
11681168
}
11691169

1170+
if name == "simd_bitmask" {
1171+
// The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
1172+
// vector mask and returns an unsigned integer containing the most
1173+
// significant bit (MSB) of each lane.
1174+
use rustc_target::abi::HasDataLayout;
1175+
1176+
// If the vector has less than 8 lanes, an u8 is returned with zeroed
1177+
// trailing bits.
1178+
let expected_int_bits = in_len.max(8);
1179+
match ret_ty.sty {
1180+
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (),
1181+
_ => return_error!(
1182+
"bitmask `{}`, expected `u{}`",
1183+
ret_ty, expected_int_bits
1184+
),
1185+
}
1186+
1187+
// Integer vector <i{in_bitwidth} x in_len>:
1188+
let (i_xn, in_elem_bitwidth) = match in_elem.sty {
1189+
ty::Int(i) => (
1190+
args[0].immediate(),
1191+
i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits() as _)
1192+
),
1193+
ty::Uint(i) => (
1194+
args[0].immediate(),
1195+
i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits() as _)
1196+
),
1197+
_ => return_error!(
1198+
"vector argument `{}`'s element type `{}`, expected integer element type",
1199+
in_ty, in_elem
1200+
),
1201+
};
1202+
1203+
// Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
1204+
let shift_indices = vec![
1205+
bx.cx.const_int(bx.type_ix(in_elem_bitwidth as _), (in_elem_bitwidth - 1) as _); in_len
1206+
];
1207+
let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice()));
1208+
// Truncate vector to an <i1 x N>
1209+
let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len as _));
1210+
// Bitcast <i1 x N> to iN:
1211+
let i_ = bx.bitcast(i1xn, bx.type_ix(in_len as _));
1212+
// Zero-extend iN to the bitmask type:
1213+
return Ok(bx.zext(i_, bx.type_ix(expected_int_bits as _)));
1214+
}
1215+
11701216
fn simd_simple_float_intrinsic(
11711217
name: &str,
11721218
in_elem: &::rustc::ty::TyS,

src/librustc_typeck/check/intrinsic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
430430
"simd_insert" => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
431431
"simd_extract" => (2, vec![param(0), tcx.types.u32], param(1)),
432432
"simd_cast" => (2, vec![param(0)], param(1)),
433+
"simd_bitmask" => (2, vec![param(0)], param(1)),
433434
"simd_select" |
434435
"simd_select_bitmask" => (2, vec![param(0), param(1), param(1)], param(1)),
435436
"simd_reduce_all" | "simd_reduce_any" => (1, vec![param(0)], tcx.types.bool),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// compile-flags: -C no-prepopulate-passes
2+
// ignore-tidy-linelength
3+
4+
#![crate_type = "lib"]
5+
6+
#![feature(repr_simd, platform_intrinsics)]
7+
#![allow(non_camel_case_types)]
8+
9+
#[repr(simd)]
10+
#[derive(Copy, Clone)]
11+
pub struct u32x2(u32, u32);
12+
13+
#[repr(simd)]
14+
#[derive(Copy, Clone)]
15+
pub struct i32x2(i32, i32);
16+
17+
#[repr(simd)]
18+
#[derive(Copy, Clone)]
19+
pub struct i8x16(
20+
i8, i8, i8, i8, i8, i8, i8, i8,
21+
i8, i8, i8, i8, i8, i8, i8, i8,
22+
);
23+
24+
25+
extern "platform-intrinsic" {
26+
fn simd_bitmask<T, U>(x: T) -> U;
27+
}
28+
29+
// CHECK-LABEL: @bitmask_int
30+
#[no_mangle]
31+
pub unsafe fn bitmask_int(x: i32x2) -> u8 {
32+
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9]+}}, <i32 31, i32 31>
33+
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
34+
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
35+
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
36+
simd_bitmask(x)
37+
}
38+
39+
// CHECK-LABEL: @bitmask_uint
40+
#[no_mangle]
41+
pub unsafe fn bitmask_uint(x: u32x2) -> u8 {
42+
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9]+}}, <i32 31, i32 31>
43+
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
44+
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
45+
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
46+
simd_bitmask(x)
47+
}
48+
49+
// CHECK-LABEL: @bitmask_int16
50+
#[no_mangle]
51+
pub unsafe fn bitmask_int16(x: i8x16) -> u16 {
52+
// CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{[0-9]+}}, <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>
53+
// CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1>
54+
// CHECK: %{{[0-9]+}} = bitcast <16 x i1> [[B]] to i16
55+
// CHECK-NOT: zext
56+
simd_bitmask(x)
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// run-pass
2+
#![allow(non_camel_case_types)]
3+
4+
// ignore-emscripten
5+
6+
// Test that the simd_bitmask intrinsic produces correct results.
7+
8+
#![feature(repr_simd, platform_intrinsics)]
9+
#[allow(non_camel_case_types)]
10+
11+
#[repr(simd)]
12+
#[derive(Copy, Clone, PartialEq, Debug)]
13+
struct u32x4(pub u32, pub u32, pub u32, pub u32);
14+
15+
#[repr(simd)]
16+
#[derive(Copy, Clone, PartialEq, Debug)]
17+
struct u8x4(pub u8, pub u8, pub u8, pub u8);
18+
19+
#[repr(simd)]
20+
#[derive(Copy, Clone, PartialEq, Debug)]
21+
struct Tx4<T>(pub T, pub T, pub T, pub T);
22+
23+
extern "platform-intrinsic" {
24+
fn simd_bitmask<T, U>(x: T) -> U;
25+
}
26+
27+
fn main() {
28+
let z = u32x4(0, 0, 0, 0);
29+
let ez = 0_u8;
30+
31+
let o = u32x4(!0, !0, !0, !0);
32+
let eo = 0b_1111_u8;
33+
34+
let m0 = u32x4(!0, 0, !0, 0);
35+
let e0 = 0b_0000_0101_u8;
36+
37+
// Check that the MSB is extracted:
38+
let m = u8x4(0b_1000_0000, 0b_0100_0001, 0b_1100_0001, 0b_1111_1111);
39+
let e = 0b_1101;
40+
41+
// Check usize / isize
42+
let msize: Tx4<usize> = Tx4(usize::max_value(), 0, usize::max_value(), usize::max_value());
43+
44+
unsafe {
45+
let r: u8 = simd_bitmask(z);
46+
assert_eq!(r, ez);
47+
48+
let r: u8 = simd_bitmask(o);
49+
assert_eq!(r, eo);
50+
51+
let r: u8 = simd_bitmask(m0);
52+
assert_eq!(r, e0);
53+
54+
let r: u8 = simd_bitmask(m);
55+
assert_eq!(r, e);
56+
57+
let r: u8 = simd_bitmask(msize);
58+
assert_eq!(r, e);
59+
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Test that the simd_bitmask intrinsic produces ok-ish error
2+
// messages when misused.
3+
4+
#![feature(repr_simd, platform_intrinsics)]
5+
#![allow(non_camel_case_types)]
6+
7+
#[repr(simd)]
8+
#[derive(Copy, Clone)]
9+
pub struct u32x2(pub u32, pub u32);
10+
11+
#[repr(simd)]
12+
#[derive(Copy, Clone)]
13+
pub struct u32x4(pub u32, pub u32, pub u32, pub u32);
14+
15+
#[repr(simd)]
16+
#[derive(Copy, Clone)]
17+
struct u8x8(
18+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
19+
);
20+
21+
#[repr(simd)]
22+
#[derive(Copy, Clone)]
23+
struct u8x16(
24+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
25+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
26+
);
27+
28+
#[repr(simd)]
29+
#[derive(Copy, Clone)]
30+
struct u8x32(
31+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
32+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
33+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
34+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
35+
);
36+
37+
#[repr(simd)]
38+
#[derive(Copy, Clone)]
39+
struct u8x64(
40+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
41+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
42+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
43+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
44+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
45+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
46+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
47+
pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8, pub u8,
48+
);
49+
50+
extern "platform-intrinsic" {
51+
fn simd_bitmask<T, U>(x: T) -> U;
52+
}
53+
54+
fn main() {
55+
let m2 = u32x2(0, 0);
56+
let m4 = u32x4(0, 0, 0, 0);
57+
let m8 = u8x8(0, 0, 0, 0, 0, 0, 0, 0);
58+
let m16 = u8x16(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
59+
let m32 = u8x32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
61+
let m64 = u8x64(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
65+
66+
unsafe {
67+
let _: u8 = simd_bitmask(m2);
68+
let _: u8 = simd_bitmask(m4);
69+
let _: u8 = simd_bitmask(m8);
70+
let _: u16 = simd_bitmask(m16);
71+
let _: u32 = simd_bitmask(m32);
72+
let _: u64 = simd_bitmask(m64);
73+
74+
let _: u16 = simd_bitmask(m2);
75+
//~^ ERROR bitmask `u16`, expected `u8`
76+
77+
let _: u16 = simd_bitmask(m8);
78+
//~^ ERROR bitmask `u16`, expected `u8`
79+
80+
let _: u32 = simd_bitmask(m16);
81+
//~^ ERROR bitmask `u32`, expected `u16`
82+
83+
let _: u64 = simd_bitmask(m32);
84+
//~^ ERROR bitmask `u64`, expected `u32`
85+
86+
let _: u128 = simd_bitmask(m64);
87+
//~^ ERROR bitmask `u128`, expected `u64`
88+
89+
}
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u16`, expected `u8`
2+
--> $DIR/simd-intrinsic-generic-bitmask.rs:74:22
3+
|
4+
LL | let _: u16 = simd_bitmask(m2);
5+
| ^^^^^^^^^^^^^^^^
6+
7+
error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u16`, expected `u8`
8+
--> $DIR/simd-intrinsic-generic-bitmask.rs:77:22
9+
|
10+
LL | let _: u16 = simd_bitmask(m8);
11+
| ^^^^^^^^^^^^^^^^
12+
13+
error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u32`, expected `u16`
14+
--> $DIR/simd-intrinsic-generic-bitmask.rs:80:22
15+
|
16+
LL | let _: u32 = simd_bitmask(m16);
17+
| ^^^^^^^^^^^^^^^^^
18+
19+
error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u64`, expected `u32`
20+
--> $DIR/simd-intrinsic-generic-bitmask.rs:83:22
21+
|
22+
LL | let _: u64 = simd_bitmask(m32);
23+
| ^^^^^^^^^^^^^^^^^
24+
25+
error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u128`, expected `u64`
26+
--> $DIR/simd-intrinsic-generic-bitmask.rs:86:23
27+
|
28+
LL | let _: u128 = simd_bitmask(m64);
29+
| ^^^^^^^^^^^^^^^^^
30+
31+
error: aborting due to 5 previous errors
32+
33+
For more information about this error, try `rustc --explain E0511`.

0 commit comments

Comments
 (0)