@@ -20,7 +20,7 @@ use syntax::ast::Mutability;
2020use super :: {
2121 Pointer , AllocId , Allocation , GlobalId , AllocationExtra ,
2222 EvalResult , Scalar , EvalErrorKind , AllocKind , PointerArithmetic ,
23- Machine , AllocMap , MayLeak , ErrorHandled , InboundsCheck , UndefMask ,
23+ Machine , AllocMap , MayLeak , ErrorHandled , InboundsCheck ,
2424} ;
2525
2626#[ derive( Debug , PartialEq , Eq , Copy , Clone , Hash ) ]
@@ -789,38 +789,58 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
789789 // The bits have to be saved locally before writing to dest in case src and dest overlap.
790790 assert_eq ! ( size. bytes( ) as usize as u64 , size. bytes( ) ) ;
791791
792- let undef_mask = self . get ( src. alloc_id ) ?. undef_mask . clone ( ) ;
793- let get = |i| undef_mask. get ( src. offset + Size :: from_bytes ( i) ) ;
794- let dest_allocation = self . get_mut ( dest. alloc_id ) ?;
792+ let undef_mask = & self . get ( src. alloc_id ) ?. undef_mask ;
793+
794+ // a precomputed cache for ranges of defined/undefined bits
795+ // 0000010010001110 will become
796+ // [5, 1, 2, 1, 3, 3, 1]
797+ // where each element toggles the state
798+ let mut ranges = smallvec:: SmallVec :: < [ u64 ; 1 ] > :: new ( ) ;
799+ let first = undef_mask. get ( src. offset ) ;
800+ let mut cur_len = 1 ;
801+ let mut cur = first;
802+ for i in 1 ..size. bytes ( ) {
803+ // FIXME: optimize to bitshift the current undef block's bits and read the top bit
804+ if undef_mask. get ( src. offset + Size :: from_bytes ( i) ) == cur {
805+ cur_len += 1 ;
806+ } else {
807+ ranges. push ( cur_len) ;
808+ cur_len = 1 ;
809+ cur = !cur;
810+ }
811+ }
795812
813+ // now fill in all the data
814+ let dest_allocation = self . get_mut ( dest. alloc_id ) ?;
796815 // an optimization where we can just overwrite an entire range of definedness bits if
797816 // they are going to be uniformly `1` or `0`.
798- if size. bytes ( ) * repeat > UndefMask :: BLOCK_SIZE {
799- let first = undef_mask. get ( src. offset ) ;
800- // check that all bits are the same as the first bit
801- // FIXME(oli-obk): consider making this a function on `UndefMask` and optimize it, too
802- if ( 1 ..size. bytes ( ) ) . all ( |i| get ( i) == first) {
803- dest_allocation. undef_mask . set_range (
804- dest. offset ,
805- dest. offset + size * repeat,
806- first,
807- ) ;
808- return Ok ( ( ) )
809- }
817+ if ranges. is_empty ( ) {
818+ dest_allocation. undef_mask . set_range (
819+ dest. offset ,
820+ dest. offset + size * repeat,
821+ first,
822+ ) ;
823+ return Ok ( ( ) )
810824 }
811825
812- // the default path
813- for i in 0 ..size. bytes ( ) {
814- let defined = get ( i) ;
815-
816- for j in 0 ..repeat {
817- dest_allocation. undef_mask . set (
818- dest. offset + Size :: from_bytes ( i + ( size. bytes ( ) * j) ) ,
819- defined
826+ // remember to fill in the trailing bits
827+ ranges. push ( cur_len) ;
828+
829+ for mut j in 0 ..repeat {
830+ j *= size. bytes ( ) ;
831+ j += dest. offset . bytes ( ) ;
832+ let mut cur = first;
833+ for range in & ranges {
834+ let old_j = j;
835+ j += range;
836+ dest_allocation. undef_mask . set_range_inbounds (
837+ Size :: from_bytes ( old_j) ,
838+ Size :: from_bytes ( j) ,
839+ cur,
820840 ) ;
841+ cur = !cur;
821842 }
822843 }
823-
824844 Ok ( ( ) )
825845 }
826846}
0 commit comments