@@ -4,8 +4,10 @@ use rustc_middle::{mir, ty, ty::FloatTy};
44use rustc_span:: { sym, Symbol } ;
55use rustc_target:: abi:: { Endian , HasDataLayout } ;
66
7+ use crate :: helpers:: {
8+ bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool,
9+ } ;
710use crate :: * ;
8- use helpers:: { bool_to_simd_element, check_arg_count, simd_element_to_bool} ;
911
1012impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
1113pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
@@ -113,18 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
113115 }
114116 }
115117 Op :: Numeric ( name) => {
116- assert ! ( op. layout. ty. is_integral( ) ) ;
117- let size = op. layout . size ;
118- let bits = op. to_scalar ( ) . to_bits ( size) . unwrap ( ) ;
119- let extra = 128u128 . checked_sub ( u128:: from ( size. bits ( ) ) ) . unwrap ( ) ;
120- let bits_out = match name {
121- sym:: ctlz => u128:: from ( bits. leading_zeros ( ) ) . checked_sub ( extra) . unwrap ( ) ,
122- sym:: cttz => u128:: from ( ( bits << extra) . trailing_zeros ( ) ) . checked_sub ( extra) . unwrap ( ) ,
123- sym:: bswap => ( bits << extra) . swap_bytes ( ) ,
124- sym:: bitreverse => ( bits << extra) . reverse_bits ( ) ,
125- _ => unreachable ! ( ) ,
126- } ;
127- Scalar :: from_uint ( bits_out, size)
118+ this. numeric_intrinsic ( name, op. to_scalar ( ) , op. layout ) ?
128119 }
129120 } ;
130121 this. write_scalar ( val, & dest) ?;
@@ -405,37 +396,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
405396 this. write_immediate ( * val, & dest) ?;
406397 }
407398 }
399+ // Variant of `select` that takes a bitmask rather than a "vector of bool".
408400 "select_bitmask" => {
409401 let [ mask, yes, no] = check_arg_count ( args) ?;
410402 let ( yes, yes_len) = this. operand_to_simd ( yes) ?;
411403 let ( no, no_len) = this. operand_to_simd ( no) ?;
412404 let ( dest, dest_len) = this. place_to_simd ( dest) ?;
413- let bitmask_len = dest_len . max ( 8 ) ;
405+ let bitmask_len = round_to_next_multiple_of ( dest_len , 8 ) ;
414406
407+ // The mask must be an integer or an array.
408+ assert ! (
409+ mask. layout. ty. is_integral( )
410+ || matches!( mask. layout. ty. kind( ) , ty:: Array ( elemty, _) if elemty == & this. tcx. types. u8 )
411+ ) ;
415412 assert ! ( bitmask_len <= 64 ) ;
416413 assert_eq ! ( bitmask_len, mask. layout. size. bits( ) ) ;
417414 assert_eq ! ( dest_len, yes_len) ;
418415 assert_eq ! ( dest_len, no_len) ;
419416 let dest_len = u32:: try_from ( dest_len) . unwrap ( ) ;
420417 let bitmask_len = u32:: try_from ( bitmask_len) . unwrap ( ) ;
421418
422- // The mask can be a single integer or an array.
423- let mask: u64 = match mask. layout . ty . kind ( ) {
424- ty:: Int ( ..) | ty:: Uint ( ..) =>
425- this. read_scalar ( mask) ?. to_bits ( mask. layout . size ) ?. try_into ( ) . unwrap ( ) ,
426- ty:: Array ( elem, _) if matches ! ( elem. kind( ) , ty:: Uint ( ty:: UintTy :: U8 ) ) => {
427- let mask_ty = this. machine . layouts . uint ( mask. layout . size ) . unwrap ( ) ;
428- let mask = mask. transmute ( mask_ty, this) ?;
429- this. read_scalar ( & mask) ?. to_bits ( mask_ty. size ) ?. try_into ( ) . unwrap ( )
430- }
431- _ => bug ! ( "simd_select_bitmask: invalid mask type {}" , mask. layout. ty) ,
432- } ;
419+ // To read the mask, we transmute it to an integer.
420+ // That does the right thing wrt endianess.
421+ let mask_ty = this. machine . layouts . uint ( mask. layout . size ) . unwrap ( ) ;
422+ let mask = mask. transmute ( mask_ty, this) ?;
423+ let mask: u64 = this. read_scalar ( & mask) ?. to_bits ( mask_ty. size ) ?. try_into ( ) . unwrap ( ) ;
433424
434425 for i in 0 ..dest_len {
435- let mask = mask
436- & 1u64
437- . checked_shl ( simd_bitmask_index ( i, dest_len, this. data_layout ( ) . endian ) )
438- . unwrap ( ) ;
426+ let bit_i = simd_bitmask_index ( i, dest_len, this. data_layout ( ) . endian ) ;
427+ let mask = mask & 1u64 . checked_shl ( bit_i) . unwrap ( ) ;
439428 let yes = this. read_immediate ( & this. project_index ( & yes, i. into ( ) ) ?) ?;
440429 let no = this. read_immediate ( & this. project_index ( & no, i. into ( ) ) ?) ?;
441430 let dest = this. project_index ( & dest, i. into ( ) ) ?;
@@ -445,6 +434,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
445434 }
446435 for i in dest_len..bitmask_len {
447436 // If the mask is "padded", ensure that padding is all-zero.
437+ // This deliberately does not use `simd_bitmask_index`; these bits are outside
438+ // the bitmask. It does not matter in which order we check them.
448439 let mask = mask & 1u64 . checked_shl ( i) . unwrap ( ) ;
449440 if mask != 0 {
450441 throw_ub_format ! (
@@ -453,6 +444,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453444 }
454445 }
455446 }
447+ // Converts a "vector of bool" into a bitmask.
448+ "bitmask" => {
449+ let [ op] = check_arg_count ( args) ?;
450+ let ( op, op_len) = this. operand_to_simd ( op) ?;
451+ let bitmask_len = round_to_next_multiple_of ( op_len, 8 ) ;
452+
453+ // Returns either an unsigned integer or array of `u8`.
454+ assert ! (
455+ dest. layout. ty. is_integral( )
456+ || matches!( dest. layout. ty. kind( ) , ty:: Array ( elemty, _) if elemty == & this. tcx. types. u8 )
457+ ) ;
458+ assert ! ( bitmask_len <= 64 ) ;
459+ assert_eq ! ( bitmask_len, dest. layout. size. bits( ) ) ;
460+ let op_len = u32:: try_from ( op_len) . unwrap ( ) ;
461+
462+ let mut res = 0u64 ;
463+ for i in 0 ..op_len {
464+ let op = this. read_immediate ( & this. project_index ( & op, i. into ( ) ) ?) ?;
465+ if simd_element_to_bool ( op) ? {
466+ res |= 1u64
467+ . checked_shl ( simd_bitmask_index ( i, op_len, this. data_layout ( ) . endian ) )
468+ . unwrap ( ) ;
469+ }
470+ }
471+ // We have to change the type of the place to be able to write `res` into it. This
472+ // transmutes the integer to an array, which does the right thing wrt endianess.
473+ let dest =
474+ dest. transmute ( this. machine . layouts . uint ( dest. layout . size ) . unwrap ( ) , this) ?;
475+ this. write_int ( res, & dest) ?;
476+ }
456477 "cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => {
457478 let [ op] = check_arg_count ( args) ?;
458479 let ( op, op_len) = this. operand_to_simd ( op) ?;
@@ -635,34 +656,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
635656 }
636657 }
637658 }
638- "bitmask" => {
639- let [ op] = check_arg_count ( args) ?;
640- let ( op, op_len) = this. operand_to_simd ( op) ?;
641- let bitmask_len = op_len. max ( 8 ) ;
642-
643- // Returns either an unsigned integer or array of `u8`.
644- assert ! (
645- dest. layout. ty. is_integral( )
646- || matches!( dest. layout. ty. kind( ) , ty:: Array ( elemty, _) if elemty == & this. tcx. types. u8 )
647- ) ;
648- assert ! ( bitmask_len <= 64 ) ;
649- assert_eq ! ( bitmask_len, dest. layout. size. bits( ) ) ;
650- let op_len = u32:: try_from ( op_len) . unwrap ( ) ;
651-
652- let mut res = 0u64 ;
653- for i in 0 ..op_len {
654- let op = this. read_immediate ( & this. project_index ( & op, i. into ( ) ) ?) ?;
655- if simd_element_to_bool ( op) ? {
656- res |= 1u64
657- . checked_shl ( simd_bitmask_index ( i, op_len, this. data_layout ( ) . endian ) )
658- . unwrap ( ) ;
659- }
660- }
661- // We have to force the place type to be an int so that we can write `res` into it.
662- let mut dest = this. force_allocation ( dest) ?;
663- dest. layout = this. machine . layouts . uint ( dest. layout . size ) . unwrap ( ) ;
664- this. write_int ( res, & dest) ?;
665- }
666659
667660 name => throw_unsup_format ! ( "unimplemented intrinsic: `simd_{name}`" ) ,
668661 }
0 commit comments