@@ -458,26 +458,48 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
458
458
) ;
459
459
}
460
460
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( ) ) ;
467
461
assert_eq ! ( dest_len, yes_len) ;
468
462
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 ( ) ;
471
463
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
+ } ;
477
497
498
+ let dest_len = u32:: try_from ( dest_len) . unwrap ( ) ;
499
+ let bitmask_len = u32:: try_from ( bitmask_len) . unwrap ( ) ;
478
500
for i in 0 ..dest_len {
479
501
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) ;
481
503
let yes = this. read_immediate ( & this. project_index ( & yes, i. into ( ) ) ?) ?;
482
504
let no = this. read_immediate ( & this. project_index ( & no, i. into ( ) ) ?) ?;
483
505
let dest = this. project_index ( & dest, i. into ( ) ) ?;
@@ -489,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
489
511
// If the mask is "padded", ensure that padding is all-zero.
490
512
// This deliberately does not use `simd_bitmask_index`; these bits are outside
491
513
// 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 ) ;
493
515
if mask != 0 {
494
516
throw_ub_format ! (
495
517
"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> {
508
530
) ;
509
531
}
510
532
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( ) ) ;
517
533
let op_len = u32:: try_from ( op_len) . unwrap ( ) ;
518
-
519
534
let mut res = 0u64 ;
520
535
for i in 0 ..op_len {
521
536
let op = this. read_immediate ( & this. project_index ( & op, i. into ( ) ) ?) ?;
522
537
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 ( ) ) ?;
526
567
}
568
+ _ => bug ! ( "simd_bitmask: invalid return type {}" , dest. layout. ty) ,
527
569
}
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) ?;
533
570
}
534
571
"cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => {
535
572
let [ op] = check_arg_count ( args) ?;
@@ -615,8 +652,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
615
652
616
653
let val = if src_index < left_len {
617
654
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) ;
620
657
this. read_immediate ( & this. project_index ( & right, right_idx) ?) ?
621
658
} else {
622
659
throw_ub_format ! (
@@ -655,8 +692,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
655
692
656
693
let val = if src_index < left_len {
657
694
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) ;
660
697
this. read_immediate ( & this. project_index ( & right, right_idx) ?) ?
661
698
} else {
662
699
throw_ub_format ! (
0 commit comments