1
1
//! The virtual memory representation of the MIR interpreter.
2
2
3
3
use std:: borrow:: Cow ;
4
- use std:: convert:: TryFrom ;
4
+ use std:: convert:: { TryFrom , TryInto } ;
5
5
use std:: iter;
6
6
use std:: ops:: { Deref , Range } ;
7
7
use std:: ptr;
@@ -720,13 +720,12 @@ impl InitMask {
720
720
return Err ( self . len ..end) ;
721
721
}
722
722
723
- // FIXME(oli-obk): optimize this for allocations larger than a block.
724
- let idx = ( start..end) . find ( |& i| !self . get ( i) ) ;
723
+ let uninit_start = find_bit ( self , start, end, false ) ;
725
724
726
- match idx {
727
- Some ( idx ) => {
728
- let uninit_end = ( idx.. end) . find ( | & i| self . get ( i ) ) . unwrap_or ( end) ;
729
- Err ( idx ..uninit_end)
725
+ match uninit_start {
726
+ Some ( uninit_start ) => {
727
+ let uninit_end = find_bit ( self , uninit_start , end, true ) . unwrap_or ( end) ;
728
+ Err ( uninit_start ..uninit_end)
730
729
}
731
730
None => Ok ( ( ) ) ,
732
731
}
@@ -863,9 +862,8 @@ impl<'a> Iterator for InitChunkIter<'a> {
863
862
}
864
863
865
864
let is_init = self . init_mask . get ( self . start ) ;
866
- // FIXME(oli-obk): optimize this for allocations larger than a block.
867
865
let end_of_chunk =
868
- ( self . start .. self . end ) . find ( | & i| self . init_mask . get ( i ) != is_init) . unwrap_or ( self . end ) ;
866
+ find_bit ( & self . init_mask , self . start , self . end , ! is_init) . unwrap_or ( self . end ) ;
869
867
let range = self . start ..end_of_chunk;
870
868
871
869
self . start = end_of_chunk;
@@ -874,10 +872,105 @@ impl<'a> Iterator for InitChunkIter<'a> {
874
872
}
875
873
}
876
874
875
+ /// Returns the index of the first bit in `start..end` (end-exclusive) that is equal to is_init.
876
+ fn find_bit ( init_mask : & InitMask , start : Size , end : Size , is_init : bool ) -> Option < Size > {
877
+ fn find_bit_fast ( init_mask : & InitMask , start : Size , end : Size , is_init : bool ) -> Option < Size > {
878
+ fn search_block (
879
+ bits : Block ,
880
+ block : usize ,
881
+ start_bit : usize ,
882
+ is_init : bool ,
883
+ ) -> Option < Size > {
884
+ // invert bits so we're always looking for the first set bit
885
+ let bits = if is_init { bits } else { !bits } ;
886
+ // mask off unused start bits
887
+ let bits = bits & ( !0 << start_bit) ;
888
+ // find set bit, if any
889
+ if bits == 0 {
890
+ None
891
+ } else {
892
+ let bit = bits. trailing_zeros ( ) ;
893
+ Some ( size_from_bit_index ( block, bit) )
894
+ }
895
+ }
896
+
897
+ if start >= end {
898
+ return None ;
899
+ }
900
+
901
+ let ( start_block, start_bit) = bit_index ( start) ;
902
+ let ( end_block, end_bit) = bit_index ( end) ;
903
+
904
+ // handle first block: need to skip `start_bit` bits
905
+ if let Some ( i) =
906
+ search_block ( init_mask. blocks [ start_block] , start_block, start_bit, is_init)
907
+ {
908
+ if i < end {
909
+ return Some ( i) ;
910
+ } else {
911
+ // if the range is less than a block, we may find a matching bit after `end`
912
+ return None ;
913
+ }
914
+ }
915
+
916
+ let one_block_past_the_end = if end_bit > 0 {
917
+ // if `end_bit` > 0, then the range overlaps `end_block`
918
+ end_block + 1
919
+ } else {
920
+ end_block
921
+ } ;
922
+
923
+ // handle remaining blocks
924
+ if start_block < one_block_past_the_end {
925
+ for ( & bits, block) in init_mask. blocks [ start_block + 1 ..one_block_past_the_end]
926
+ . iter ( )
927
+ . zip ( start_block + 1 ..)
928
+ {
929
+ if let Some ( i) = search_block ( bits, block, 0 , is_init) {
930
+ if i < end {
931
+ return Some ( i) ;
932
+ } else {
933
+ // if this is the last block, we may find a matching bit after `end`
934
+ return None ;
935
+ }
936
+ }
937
+ }
938
+ }
939
+
940
+ None
941
+ }
942
+
943
+ #[ cfg_attr( not( debug_assertions) , allow( dead_code) ) ]
944
+ fn find_bit_slow ( init_mask : & InitMask , start : Size , end : Size , is_init : bool ) -> Option < Size > {
945
+ ( start..end) . find ( |& i| init_mask. get ( i) == is_init)
946
+ }
947
+
948
+ let result = find_bit_fast ( init_mask, start, end, is_init) ;
949
+
950
+ debug_assert_eq ! (
951
+ result,
952
+ find_bit_slow( init_mask, start, end, is_init) ,
953
+ "optimized implementation of find_bit is wrong for start={:?} end={:?} is_init={} init_mask={:#?}" ,
954
+ start,
955
+ end,
956
+ is_init,
957
+ init_mask
958
+ ) ;
959
+
960
+ result
961
+ }
962
+
877
963
#[ inline]
878
964
fn bit_index ( bits : Size ) -> ( usize , usize ) {
879
965
let bits = bits. bytes ( ) ;
880
966
let a = bits / InitMask :: BLOCK_SIZE ;
881
967
let b = bits % InitMask :: BLOCK_SIZE ;
882
968
( usize:: try_from ( a) . unwrap ( ) , usize:: try_from ( b) . unwrap ( ) )
883
969
}
970
+
971
+ #[ inline]
972
+ fn size_from_bit_index ( block : impl TryInto < u64 > , bit : impl TryInto < u64 > ) -> Size {
973
+ let block = block. try_into ( ) . ok ( ) . unwrap ( ) ;
974
+ let bit = bit. try_into ( ) . ok ( ) . unwrap ( ) ;
975
+ Size :: from_bytes ( block * InitMask :: BLOCK_SIZE + bit)
976
+ }
0 commit comments