@@ -458,26 +458,48 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
458458 ) ;
459459 }
460460
461- // The mask must be an integer or an array.
462- assert ! (
463- mask. layout. ty. is_integral( )
464- || matches!( mask. layout. ty. kind( ) , ty:: Array ( elemty, _) if elemty == & this. tcx. types. u8 )
465- ) ;
466- assert_eq ! ( bitmask_len, mask. layout. size. bits( ) ) ;
467461 assert_eq ! ( dest_len, yes_len) ;
468462 assert_eq ! ( dest_len, no_len) ;
469- let dest_len = u32:: try_from ( dest_len) . unwrap ( ) ;
470- let bitmask_len = u32:: try_from ( bitmask_len) . unwrap ( ) ;
471463
472- // To read the mask, we transmute it to an integer.
473- // That does the right thing wrt endianness.
474- let mask_ty = this. machine . layouts . uint ( mask. layout . size ) . unwrap ( ) ;
475- let mask = mask. transmute ( mask_ty, this) ?;
476- let mask: u64 = this. read_scalar ( & mask) ?. to_bits ( mask_ty. size ) ?. try_into ( ) . unwrap ( ) ;
464+ // Read the mask, either as an integer or as an array.
465+ let mask: u64 = match mask. layout . ty . kind ( ) {
466+ ty:: Uint ( _) => {
467+ // Any larger integer type is fine.
468+ assert ! ( mask. layout. size. bits( ) >= bitmask_len) ;
469+ this. read_scalar ( mask) ?. to_bits ( mask. layout . size ) ?. try_into ( ) . unwrap ( )
470+ }
471+ ty:: Array ( elem, _len) if elem == & this. tcx . types . u8 => {
472+ // The array must have exactly the right size.
473+ assert_eq ! ( mask. layout. size. bits( ) , bitmask_len) ;
474+ // Read the raw bytes.
475+ let mask = mask. assert_mem_place ( ) ; // arrays cannot be immediate
476+ let mask_bytes =
477+ this. read_bytes_ptr_strip_provenance ( mask. ptr ( ) , mask. layout . size ) ?;
478+ // Turn them into a `u64` in the right way.
479+ let mask_size = mask. layout . size . bytes_usize ( ) ;
480+ let mut mask_arr = [ 0u8 ; 8 ] ;
481+ match this. data_layout ( ) . endian {
482+ Endian :: Little => {
483+ // Fill the first N bytes.
484+ mask_arr[ ..mask_size] . copy_from_slice ( mask_bytes) ;
485+ u64:: from_le_bytes ( mask_arr)
486+ }
487+ Endian :: Big => {
488+ // Fill the last N bytes.
489+ let i = mask_arr. len ( ) . strict_sub ( mask_size) ;
490+ mask_arr[ i..] . copy_from_slice ( mask_bytes) ;
491+ u64:: from_be_bytes ( mask_arr)
492+ }
493+ }
494+ }
495+ _ => bug ! ( "simd_select_bitmask: invalid mask type {}" , mask. layout. ty) ,
496+ } ;
477497
498+ let dest_len = u32:: try_from ( dest_len) . unwrap ( ) ;
499+ let bitmask_len = u32:: try_from ( bitmask_len) . unwrap ( ) ;
478500 for i in 0 ..dest_len {
479501 let bit_i = simd_bitmask_index ( i, dest_len, this. data_layout ( ) . endian ) ;
480- let mask = mask & 1u64 . checked_shl ( bit_i) . unwrap ( ) ;
502+ let mask = mask & 1u64 . strict_shl ( bit_i) ;
481503 let yes = this. read_immediate ( & this. project_index ( & yes, i. into ( ) ) ?) ?;
482504 let no = this. read_immediate ( & this. project_index ( & no, i. into ( ) ) ?) ?;
483505 let dest = this. project_index ( & dest, i. into ( ) ) ?;
@@ -489,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
489511 // If the mask is "padded", ensure that padding is all-zero.
490512 // This deliberately does not use `simd_bitmask_index`; these bits are outside
491513 // the bitmask. It does not matter in which order we check them.
492- let mask = mask & 1u64 . checked_shl ( i ) . unwrap ( ) ;
514+ let mask = mask & 1u64 . strict_shl ( i ) ;
493515 if mask != 0 {
494516 throw_ub_format ! (
495517 "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits"
@@ -508,28 +530,43 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
508530 ) ;
509531 }
510532
511- // Returns either an unsigned integer or array of `u8`.
512- assert ! (
513- dest. layout. ty. is_integral( )
514- || matches!( dest. layout. ty. kind( ) , ty:: Array ( elemty, _) if elemty == & this. tcx. types. u8 )
515- ) ;
516- assert_eq ! ( bitmask_len, dest. layout. size. bits( ) ) ;
517533 let op_len = u32:: try_from ( op_len) . unwrap ( ) ;
518-
519534 let mut res = 0u64 ;
520535 for i in 0 ..op_len {
521536 let op = this. read_immediate ( & this. project_index ( & op, i. into ( ) ) ?) ?;
522537 if simd_element_to_bool ( op) ? {
523- res |= 1u64
524- . checked_shl ( simd_bitmask_index ( i, op_len, this. data_layout ( ) . endian ) )
525- . unwrap ( ) ;
538+ let bit_i = simd_bitmask_index ( i, op_len, this. data_layout ( ) . endian ) ;
539+ res |= 1u64 . strict_shl ( bit_i) ;
540+ }
541+ }
542+ // Write the result, depending on the `dest` type.
543+ // Returns either an unsigned integer or array of `u8`.
544+ match dest. layout . ty . kind ( ) {
545+ ty:: Uint ( _) => {
546+ // Any larger integer type is fine, it will be zero-extended.
547+ assert ! ( dest. layout. size. bits( ) >= bitmask_len) ;
548+ this. write_int ( res, dest) ?;
549+ }
550+ ty:: Array ( elem, _len) if elem == & this. tcx . types . u8 => {
551+ // The array must have exactly the right size.
552+ assert_eq ! ( dest. layout. size. bits( ) , bitmask_len) ;
553+ // We have to write the result byte-for-byte.
554+ let res_size = dest. layout . size . bytes_usize ( ) ;
555+ let res_bytes;
556+ let res_bytes_slice = match this. data_layout ( ) . endian {
557+ Endian :: Little => {
558+ res_bytes = res. to_le_bytes ( ) ;
559+ & res_bytes[ ..res_size] // take the first N bytes
560+ }
561+ Endian :: Big => {
562+ res_bytes = res. to_be_bytes ( ) ;
563+ & res_bytes[ res_bytes. len ( ) . strict_sub ( res_size) ..] // take the last N bytes
564+ }
565+ } ;
566+ this. write_bytes_ptr ( dest. ptr ( ) , res_bytes_slice. iter ( ) . cloned ( ) ) ?;
526567 }
568+ _ => bug ! ( "simd_bitmask: invalid return type {}" , dest. layout. ty) ,
527569 }
528- // We have to change the type of the place to be able to write `res` into it. This
529- // transmutes the integer to an array, which does the right thing wrt endianness.
530- let dest =
531- dest. transmute ( this. machine . layouts . uint ( dest. layout . size ) . unwrap ( ) , this) ?;
532- this. write_int ( res, & dest) ?;
533570 }
534571 "cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => {
535572 let [ op] = check_arg_count ( args) ?;
@@ -615,8 +652,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
615652
616653 let val = if src_index < left_len {
617654 this. read_immediate ( & this. project_index ( & left, src_index) ?) ?
618- } else if src_index < left_len. checked_add ( right_len) . unwrap ( ) {
619- let right_idx = src_index. checked_sub ( left_len) . unwrap ( ) ;
655+ } else if src_index < left_len. strict_add ( right_len) {
656+ let right_idx = src_index. strict_sub ( left_len) ;
620657 this. read_immediate ( & this. project_index ( & right, right_idx) ?) ?
621658 } else {
622659 throw_ub_format ! (
@@ -655,8 +692,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
655692
656693 let val = if src_index < left_len {
657694 this. read_immediate ( & this. project_index ( & left, src_index) ?) ?
658- } else if src_index < left_len. checked_add ( right_len) . unwrap ( ) {
659- let right_idx = src_index. checked_sub ( left_len) . unwrap ( ) ;
695+ } else if src_index < left_len. strict_add ( right_len) {
696+ let right_idx = src_index. strict_sub ( left_len) ;
660697 this. read_immediate ( & this. project_index ( & right, right_idx) ?) ?
661698 } else {
662699 throw_ub_format ! (
0 commit comments