Skip to content

Commit 6ba73bd

Browse files
committed
add simd_splat intrinsic
1 parent b08c11d commit 6ba73bd

9 files changed

Lines changed: 166 additions & 0 deletions

File tree

compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,31 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
348348
ret.write_cvalue(fx, ret_lane);
349349
}
350350

351+
sym::simd_splat => {
352+
intrinsic_args!(fx, args => (value); intrinsic);
353+
354+
if !ret.layout().ty.is_simd() {
355+
report_simd_type_validation_error(fx, intrinsic, span, ret.layout().ty);
356+
return;
357+
}
358+
let (lane_count, lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
359+
360+
if value.layout().ty != lane_ty {
361+
fx.tcx.dcx().span_fatal(
362+
span,
363+
format!(
364+
"[simd_splat] expected element type {lane_ty:?}, got {got:?}",
365+
got = value.layout().ty
366+
),
367+
);
368+
}
369+
370+
for i in 0..lane_count {
371+
let ret_lane = ret.place_lane(fx, i.into());
372+
ret_lane.write_cvalue(fx, value);
373+
}
374+
}
375+
351376
sym::simd_neg
352377
| sym::simd_bswap
353378
| sym::simd_bitreverse

compiler/rustc_codegen_gcc/src/intrinsic/simd.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,25 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
121121
return Ok(bx.vector_select(vector_mask, arg1, args[2].immediate()));
122122
}
123123

124+
if name == sym::simd_splat {
125+
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
126+
let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
127+
128+
require!(
129+
args[0].layout.ty == out_ty,
130+
InvalidMonomorphization::ExpectedVectorElementType {
131+
span,
132+
name,
133+
expected_element: out_ty,
134+
vector_type: ret_ty,
135+
}
136+
);
137+
138+
let elem = args[0].immediate();
139+
let elements = vec![elem; out_len as usize];
140+
return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &elements));
141+
}
142+
124143
// every intrinsic below takes a SIMD vector as its first argument
125144
require_simd!(
126145
args[0].layout.ty,

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,32 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
15881588
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
15891589
}
15901590

1591+
if name == sym::simd_splat {
1592+
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
1593+
1594+
require!(
1595+
args[0].layout.ty == out_ty,
1596+
InvalidMonomorphization::ExpectedVectorElementType {
1597+
span,
1598+
name,
1599+
expected_element: out_ty,
1600+
vector_type: ret_ty,
1601+
}
1602+
);
1603+
1604+
// `insertelement <N x elem> poison, elem %x, i32 0`
1605+
let poison_vec = bx.const_poison(llret_ty);
1606+
let idx0 = bx.const_i32(0);
1607+
let v0 = bx.insert_element(poison_vec, args[0].immediate(), idx0);
1608+
1609+
// `shufflevector <N x elem> v0, <N x elem> poison, <N x i32> zeroinitializer`
1610+
// The masks is all zeros, so this splats lane 0 (which has our element in it).
1611+
let mask = bx.const_vector(&vec![bx.const_i32(0); out_len as usize]);
1612+
let splat = bx.shuffle_vector(v0, poison_vec, mask);
1613+
1614+
return Ok(splat);
1615+
}
1616+
15911617
// every intrinsic below takes a SIMD vector as its first argument
15921618
let (in_len, in_elem) = require_simd!(args[0].layout.ty, SimdInput);
15931619
let in_ty = args[0].layout.ty;

compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6060
}
6161
self.copy_op(&self.project_index(&input, index)?, &dest)?;
6262
}
63+
sym::simd_splat => {
64+
let elem = &args[0];
65+
let (dest, dest_len) = self.project_to_simd(&dest)?;
66+
67+
for i in 0..dest_len {
68+
let place = self.project_index(&dest, i)?;
69+
self.copy_op(elem, &place)?;
70+
}
71+
}
6372
sym::simd_neg
6473
| sym::simd_fabs
6574
| sym::simd_ceil

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ pub(crate) fn check_intrinsic_type(
746746
sym::simd_extract | sym::simd_extract_dyn => {
747747
(2, 0, vec![param(0), tcx.types.u32], param(1))
748748
}
749+
sym::simd_splat => (2, 0, vec![param(1)], param(0)),
749750
sym::simd_cast
750751
| sym::simd_as
751752
| sym::simd_cast_ptr

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,7 @@ symbols! {
21322132
simd_shr,
21332133
simd_shuffle,
21342134
simd_shuffle_const_generic,
2135+
simd_splat,
21352136
simd_sub,
21362137
simd_trunc,
21372138
simd_with_exposed_provenance,

library/core/src/intrinsics/simd.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ pub unsafe fn simd_extract_dyn<T, U>(x: T, idx: u32) -> U {
5959
unsafe { (&raw const x).cast::<U>().add(idx as usize).read() }
6060
}
6161

62+
#[rustc_nounwind]
63+
#[rustc_intrinsic]
64+
pub const unsafe fn simd_splat<T, U>(value: U) -> T;
65+
6266
/// Adds two simd vectors elementwise.
6367
///
6468
/// `T` must be a vector of integers or floats.

tests/codegen-llvm/simd/splat.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![crate_type = "lib"]
2+
#![no_std]
3+
#![feature(repr_simd, core_intrinsics)]
4+
use core::intrinsics::simd::simd_splat;
5+
use core::{mem, ptr};
6+
7+
#[path = "../../auxiliary/minisimd.rs"]
8+
mod minisimd;
9+
use minisimd::*;
10+
11+
#[no_mangle]
12+
unsafe extern "C" fn int(x: u16) -> u16x2 {
13+
// CHECK-LABEL: int
14+
// CHECK: start:
15+
// CHECK-NEXT: %0 = insertelement <2 x i16> poison, i16 %x, i64 0
16+
// CHECK-NEXT: %1 = shufflevector <2 x i16> %0, <2 x i16> poison, <2 x i32> zeroinitializer
17+
// CHECK-NEXT: ret
18+
simd_splat(x)
19+
}
20+
21+
#[no_mangle]
22+
unsafe extern "C" fn float(x: f32) -> f32x4 {
23+
// CHECK-LABEL: float
24+
// CHECK: start:
25+
// CHECK-NEXT: %0 = insertelement <4 x float> poison, float %x, i64 0
26+
// CHECK-NEXT: %1 = shufflevector <4 x float> %0, <4 x float> poison, <4 x i32> zeroinitializer
27+
// CHECK-NEXT: ret
28+
simd_splat(x)
29+
}

tests/ui/simd/intrinsic/splat.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//@ run-pass
2+
#![feature(repr_simd, core_intrinsics)]
3+
4+
#[path = "../../../auxiliary/minisimd.rs"]
5+
mod minisimd;
6+
use minisimd::*;
7+
8+
use std::intrinsics::simd::simd_splat;
9+
10+
fn main() {
11+
unsafe {
12+
let x: Simd<u32, 1> = simd_splat(123u32);
13+
let y: Simd<u32, 1> = const { simd_splat(123u32) };
14+
assert_eq!(x.into_array(), [123; 1]);
15+
assert_eq!(x.into_array(), y.into_array());
16+
17+
let x: u16x2 = simd_splat(42u16);
18+
let y: u16x2 = const { simd_splat(42u16) };
19+
assert_eq!(x.into_array(), [42; 2]);
20+
assert_eq!(x.into_array(), y.into_array());
21+
22+
let x: u128x4 = simd_splat(42u128);
23+
let y: u128x4 = const { simd_splat(42u128) };
24+
assert_eq!(x.into_array(), [42; 4]);
25+
assert_eq!(x.into_array(), y.into_array());
26+
27+
let x: i32x4 = simd_splat(-7i32);
28+
let y: i32x4 = const { simd_splat(-7i32) };
29+
assert_eq!(x.into_array(), [-7; 4]);
30+
assert_eq!(x.into_array(), y.into_array());
31+
32+
let x: f32x4 = simd_splat(42.0f32);
33+
let y: f32x4 = const { simd_splat(42.0f32) };
34+
assert_eq!(x.into_array(), [42.0; 4]);
35+
assert_eq!(x.into_array(), y.into_array());
36+
37+
let x: f64x2 = simd_splat(42.0f64);
38+
let y: f64x2 = const { simd_splat(42.0f64) };
39+
assert_eq!(x.into_array(), [42.0; 2]);
40+
assert_eq!(x.into_array(), y.into_array());
41+
42+
static ZERO: u8 = 0u8;
43+
let x: Simd<*const u8, 2> = simd_splat(&raw const ZERO);
44+
assert_eq!(x.into_array(), [&raw const ZERO; 2]);
45+
46+
// FIXME: this hits "could not evaluate shuffle_indices at compile time",
47+
// emitted in `immediate_const_vector`. const-eval should be able to handle
48+
// this though I think? `const { [&raw const ZERO; 2] }` appears to work.
49+
// let y: Simd<*const u8, 2> = const { simd_splat(&raw const ZERO) };
50+
// assert_eq!(x.into_array(), y.into_array());
51+
}
52+
}

0 commit comments

Comments
 (0)