@@ -4,8 +4,10 @@ use rustc_middle::{mir, ty, ty::FloatTy};
4
4
use rustc_span:: { sym, Symbol } ;
5
5
use rustc_target:: abi:: { Endian , HasDataLayout } ;
6
6
7
+ use crate :: helpers:: {
8
+ bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool,
9
+ } ;
7
10
use crate :: * ;
8
- use helpers:: { bool_to_simd_element, check_arg_count, simd_element_to_bool} ;
9
11
10
12
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
11
13
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
@@ -113,18 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
113
115
}
114
116
}
115
117
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 ) ?
128
119
}
129
120
} ;
130
121
this. write_scalar ( val, & dest) ?;
@@ -405,37 +396,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
405
396
this. write_immediate ( * val, & dest) ?;
406
397
}
407
398
}
399
+ // Variant of `select` that takes a bitmask rather than a "vector of bool".
408
400
"select_bitmask" => {
409
401
let [ mask, yes, no] = check_arg_count ( args) ?;
410
402
let ( yes, yes_len) = this. operand_to_simd ( yes) ?;
411
403
let ( no, no_len) = this. operand_to_simd ( no) ?;
412
404
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 ) ;
414
406
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
+ ) ;
415
412
assert ! ( bitmask_len <= 64 ) ;
416
413
assert_eq ! ( bitmask_len, mask. layout. size. bits( ) ) ;
417
414
assert_eq ! ( dest_len, yes_len) ;
418
415
assert_eq ! ( dest_len, no_len) ;
419
416
let dest_len = u32:: try_from ( dest_len) . unwrap ( ) ;
420
417
let bitmask_len = u32:: try_from ( bitmask_len) . unwrap ( ) ;
421
418
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 ( ) ;
433
424
434
425
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 ( ) ;
439
428
let yes = this. read_immediate ( & this. project_index ( & yes, i. into ( ) ) ?) ?;
440
429
let no = this. read_immediate ( & this. project_index ( & no, i. into ( ) ) ?) ?;
441
430
let dest = this. project_index ( & dest, i. into ( ) ) ?;
@@ -445,6 +434,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
445
434
}
446
435
for i in dest_len..bitmask_len {
447
436
// 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.
448
439
let mask = mask & 1u64 . checked_shl ( i) . unwrap ( ) ;
449
440
if mask != 0 {
450
441
throw_ub_format ! (
@@ -453,6 +444,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453
444
}
454
445
}
455
446
}
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
+ }
456
477
"cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => {
457
478
let [ op] = check_arg_count ( args) ?;
458
479
let ( op, op_len) = this. operand_to_simd ( op) ?;
@@ -635,34 +656,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
635
656
}
636
657
}
637
658
}
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
- }
666
659
667
660
name => throw_unsup_format ! ( "unimplemented intrinsic: `simd_{name}`" ) ,
668
661
}
0 commit comments