|
| 1 | +use std::convert::TryInto; |
1 | 2 | use std::iter;
|
2 | 3 |
|
3 | 4 | use log::trace;
|
4 | 5 |
|
5 | 6 | use rustc_apfloat::{Float, Round};
|
6 | 7 | use rustc_middle::ty::layout::{HasParamEnv, IntegerExt, LayoutOf};
|
7 | 8 | use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy};
|
8 |
| -use rustc_target::abi::{Align, Integer}; |
| 9 | +use rustc_target::abi::{Align, Endian, HasDataLayout, Integer, Size}; |
9 | 10 |
|
10 | 11 | use crate::*;
|
11 |
| -use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool}; |
| 12 | +use helpers::check_arg_count; |
12 | 13 |
|
13 | 14 | pub enum AtomicOp {
|
14 | 15 | MirOp(mir::BinOp, bool),
|
@@ -663,6 +664,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
663 | 664 | this.write_immediate(*val, &dest.into())?;
|
664 | 665 | }
|
665 | 666 | }
|
| 667 | + "simd_select_bitmask" => { |
| 668 | + let &[ref mask, ref yes, ref no] = check_arg_count(args)?; |
| 669 | + let (yes, yes_len) = this.operand_to_simd(yes)?; |
| 670 | + let (no, no_len) = this.operand_to_simd(no)?; |
| 671 | + let (dest, dest_len) = this.place_to_simd(dest)?; |
| 672 | + let bitmask_len = dest_len.max(8); |
| 673 | + |
| 674 | + assert!(mask.layout.ty.is_integral()); |
| 675 | + assert!(bitmask_len <= 64); |
| 676 | + assert_eq!(bitmask_len, mask.layout.size.bits()); |
| 677 | + assert_eq!(dest_len, yes_len); |
| 678 | + assert_eq!(dest_len, no_len); |
| 679 | + |
| 680 | + let mask: u64 = this |
| 681 | + .read_scalar(mask)? |
| 682 | + .check_init()? |
| 683 | + .to_bits(mask.layout.size)? |
| 684 | + .try_into() |
| 685 | + .unwrap(); |
| 686 | + for i in 0..dest_len { |
| 687 | + let mask = |
| 688 | + mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian)); |
| 689 | + let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; |
| 690 | + let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; |
| 691 | + let dest = this.mplace_index(&dest, i)?; |
| 692 | + |
| 693 | + let val = if mask != 0 { yes } else { no }; |
| 694 | + this.write_immediate(*val, &dest.into())?; |
| 695 | + } |
| 696 | + for i in dest_len..bitmask_len { |
| 697 | + // If the mask is "padded", ensure that padding is all-zero. |
| 698 | + let mask = mask & (1 << i); |
| 699 | + if mask != 0 { |
| 700 | + throw_ub_format!( |
| 701 | + "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" |
| 702 | + ); |
| 703 | + } |
| 704 | + } |
| 705 | + } |
666 | 706 | #[rustfmt::skip]
|
667 | 707 | "simd_cast" | "simd_as" => {
|
668 | 708 | let &[ref op] = check_arg_count(args)?;
|
@@ -787,6 +827,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
787 | 827 | }
|
788 | 828 | }
|
789 | 829 | }
|
| 830 | + "simd_bitmask" => { |
| 831 | + let &[ref op] = check_arg_count(args)?; |
| 832 | + let (op, op_len) = this.operand_to_simd(op)?; |
| 833 | + let bitmask_len = op_len.max(8); |
| 834 | + |
| 835 | + assert!(dest.layout.ty.is_integral()); |
| 836 | + assert!(bitmask_len <= 64); |
| 837 | + assert_eq!(bitmask_len, dest.layout.size.bits()); |
| 838 | + |
| 839 | + let mut res = 0u64; |
| 840 | + for i in 0..op_len { |
| 841 | + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; |
| 842 | + if simd_element_to_bool(op)? { |
| 843 | + res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian); |
| 844 | + } |
| 845 | + } |
| 846 | + this.write_int(res, dest)?; |
| 847 | + } |
790 | 848 |
|
791 | 849 | // Atomic operations
|
792 | 850 | "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?,
|
@@ -1307,3 +1365,26 @@ fn fmin_op<'tcx>(
|
1307 | 1365 | FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)),
|
1308 | 1366 | })
|
1309 | 1367 | }
|
| 1368 | + |
| 1369 | +fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Tag> { |
| 1370 | + // SIMD uses all-1 as pattern for "true" |
| 1371 | + let val = if b { -1 } else { 0 }; |
| 1372 | + Scalar::from_int(val, size) |
| 1373 | +} |
| 1374 | + |
| 1375 | +fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool> { |
| 1376 | + let val = elem.to_scalar()?.to_int(elem.layout.size)?; |
| 1377 | + Ok(match val { |
| 1378 | + 0 => false, |
| 1379 | + -1 => true, |
| 1380 | + _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), |
| 1381 | + }) |
| 1382 | +} |
| 1383 | + |
| 1384 | +fn simd_bitmask_index(idx: u64, vec_len: u64, endianess: Endian) -> u64 { |
| 1385 | + assert!(idx < vec_len); |
| 1386 | + match endianess { |
| 1387 | + Endian::Little => idx, |
| 1388 | + Endian::Big => vec_len - 1 - idx, // reverse order of bits |
| 1389 | + } |
| 1390 | +} |
0 commit comments