Skip to content

Commit a35877b

Browse files
committed
Auto merge of #2013 - RalfJung:simd, r=RalfJung
implement simd_{shuffle,gather,scatter} This makes portable-simd doctests pass. :) Cc #1912
2 parents 5aeff5d + 41ffce1 commit a35877b

File tree

4 files changed

+129
-3
lines changed

4 files changed

+129
-3
lines changed

src/shims/intrinsics.rs

+87-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::iter;
33
use log::trace;
44

55
use rustc_apfloat::{Float, Round};
6-
use rustc_middle::ty::layout::{IntegerExt, LayoutOf};
6+
use rustc_middle::ty::layout::{HasParamEnv, IntegerExt, LayoutOf};
77
use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy};
88
use rustc_target::abi::{Align, Integer};
99

@@ -570,8 +570,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
570570
let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?;
571571
let dest = this.mplace_index(&dest, i)?;
572572

573-
let mask = simd_element_to_bool(mask)?;
574-
let val = if mask { yes } else { no };
573+
let val = if simd_element_to_bool(mask)? { yes } else { no };
575574
this.write_immediate(*val, &dest.into())?;
576575
}
577576
}
@@ -614,6 +613,91 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
614613
this.write_immediate(val, &dest.into())?;
615614
}
616615
}
616+
"simd_shuffle" => {
617+
let &[ref left, ref right, ref index] = check_arg_count(args)?;
618+
let (left, left_len) = this.operand_to_simd(left)?;
619+
let (right, right_len) = this.operand_to_simd(right)?;
620+
let (dest, dest_len) = this.place_to_simd(dest)?;
621+
622+
// `index` is an array, not a SIMD type
623+
let ty::Array(_, index_len) = index.layout.ty.kind() else {
624+
bug!("simd_shuffle index argument has non-array type {}", index.layout.ty)
625+
};
626+
let index_len = index_len.eval_usize(*this.tcx, this.param_env());
627+
628+
assert_eq!(left_len, right_len);
629+
assert_eq!(index_len, dest_len);
630+
631+
for i in 0..dest_len {
632+
let src_index: u64 = this
633+
.read_immediate(&this.operand_index(&index, i)?.into())?
634+
.to_scalar()?
635+
.to_u32()?
636+
.into();
637+
let dest = this.mplace_index(&dest, i)?;
638+
639+
let val = if src_index < left_len {
640+
this.read_immediate(&this.mplace_index(&left, src_index)?.into())?
641+
} else if src_index < left_len.checked_add(right_len).unwrap() {
642+
this.read_immediate(
643+
&this.mplace_index(&right, src_index - left_len)?.into(),
644+
)?
645+
} else {
646+
bug!(
647+
"simd_shuffle index {} is out of bounds for 2 vectors of size {}",
648+
src_index,
649+
left_len
650+
);
651+
};
652+
this.write_immediate(*val, &dest.into())?;
653+
}
654+
}
655+
"simd_gather" => {
656+
let &[ref passthru, ref ptrs, ref mask] = check_arg_count(args)?;
657+
let (passthru, passthru_len) = this.operand_to_simd(passthru)?;
658+
let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
659+
let (mask, mask_len) = this.operand_to_simd(mask)?;
660+
let (dest, dest_len) = this.place_to_simd(dest)?;
661+
662+
assert_eq!(dest_len, passthru_len);
663+
assert_eq!(dest_len, ptrs_len);
664+
assert_eq!(dest_len, mask_len);
665+
666+
for i in 0..dest_len {
667+
let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?;
668+
let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
669+
let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
670+
let dest = this.mplace_index(&dest, i)?;
671+
672+
let val = if simd_element_to_bool(mask)? {
673+
let place = this.deref_operand(&ptr.into())?;
674+
this.read_immediate(&place.into())?
675+
} else {
676+
passthru
677+
};
678+
this.write_immediate(*val, &dest.into())?;
679+
}
680+
}
681+
"simd_scatter" => {
682+
let &[ref value, ref ptrs, ref mask] = check_arg_count(args)?;
683+
let (value, value_len) = this.operand_to_simd(value)?;
684+
let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?;
685+
let (mask, mask_len) = this.operand_to_simd(mask)?;
686+
687+
assert_eq!(ptrs_len, value_len);
688+
assert_eq!(ptrs_len, mask_len);
689+
690+
for i in 0..ptrs_len {
691+
let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?;
692+
let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?;
693+
let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?;
694+
695+
if simd_element_to_bool(mask)? {
696+
let place = this.deref_operand(&ptr.into())?;
697+
this.write_immediate(*value, &place.into())?;
698+
}
699+
}
700+
}
617701

618702
// Atomic operations
619703
"atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds
2+
#![feature(portable_simd)]
3+
use std::simd::*;
4+
5+
fn main() { unsafe {
6+
let vec: &[i8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18];
7+
let idxs = Simd::from_array([9, 3, 0, 17]);
8+
let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0));
9+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds
2+
#![feature(portable_simd)]
3+
use std::simd::*;
4+
5+
fn main() { unsafe {
6+
let mut vec: Vec<i8> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
7+
let idxs = Simd::from_array([9, 3, 0, 17]);
8+
Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked(&mut vec, Mask::splat(true), idxs);
9+
} }

tests/run-pass/portable-simd.rs

+24
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,28 @@ fn simd_cast() {
238238
}
239239
}
240240

241+
fn simd_swizzle() {
242+
use Which::*;
243+
244+
let a = f32x4::splat(10.0);
245+
let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]);
246+
247+
assert_eq!(simd_swizzle!(b, [3, 0, 0, 2]), f32x4::from_array([-4.0, 1.0, 1.0, 3.0]));
248+
assert_eq!(simd_swizzle!(b, [1, 2]), f32x2::from_array([2.0, 3.0]));
249+
assert_eq!(simd_swizzle!(b, a, [First(3), Second(0)]), f32x2::from_array([-4.0, 10.0]));
250+
}
251+
252+
fn simd_gather_scatter() {
253+
let mut vec: Vec<i16> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
254+
let idxs = Simd::from_array([9, 3, 0, 17]);
255+
let result = Simd::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
256+
assert_eq!(result, Simd::from_array([0, 13, 10, 0]));
257+
258+
let idxs = Simd::from_array([9, 3, 0, 0]);
259+
Simd::from_array([-27, 82, -41, 124]).scatter(&mut vec, idxs);
260+
assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]);
261+
}
262+
241263
fn simd_intrinsics() {
242264
extern "platform-intrinsic" {
243265
fn simd_eq<T, U>(x: T, y: T) -> U;
@@ -276,5 +298,7 @@ fn main() {
276298
simd_ops_f64();
277299
simd_ops_i32();
278300
simd_cast();
301+
simd_swizzle();
302+
simd_gather_scatter();
279303
simd_intrinsics();
280304
}

0 commit comments

Comments
 (0)