@ -7,8 +7,6 @@
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					open !  IStd  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					exception  MaximumSharingCyclicValue  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					exception  MaximumSharingLazyValue  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					module  Hashing  :  sig  
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -82,7 +80,9 @@ end = struct
 
			
		
	
		
			
				
					  module  HPhysEq  =  Caml . Hashtbl . Make  ( PhysEqualedHashedScannableBlock ) 
 
			
		
	
		
			
				
					  module  HNorm  =  Caml . Hashtbl . Make  ( HashedNormalizedScannableBlock ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  type  visited  =  Visiting  |  Normalized  of  HashedNormalizedScannableBlock . t 
 
			
		
	
		
			
				
					  type  visited  = 
 
			
		
	
		
			
				
					    |  Visiting  of  { mutable  to_patch :  ( PhysEqualedHashedScannableBlock . t  *  int )  list } 
 
			
		
	
		
			
				
					    |  Normalized  of  HashedNormalizedScannableBlock . t 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  type  t  = 
 
			
		
	
		
			
				
					    {  inplace :  bool 
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -107,15 +107,24 @@ end = struct
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let  hash_and_normalize_int  o  =  ( Hashing . of_int  ( Obj . obj  o  :  int ) ,  o ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let  dummy_should_not_be_hashed_or_used  = 
 
			
		
	
		
			
				
					    (* 
 
			
		
	
		
			
				
					      Must  be  different  than  any  block  found  in  values . 
 
			
		
	
		
			
				
					      Must  fail  if  hashed  ( there  is  actually  no  way  to  ensure  that  : (  ) ) 
 
			
		
	
		
			
				
					    * ) 
 
			
		
	
		
			
				
					    Obj . repr  ( lazy  ( assert  false ) ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  (*   
 
			
		
	
		
			
				
					    TODO :  be  much  more  efficient  and  write  it  in  C  to  be  able  to  use  the  GC  flags  to 
 
			
		
	
		
			
				
					    mark  visited  values . 
 
			
		
	
		
			
				
					   * ) 
 
			
		
	
		
			
				
					  let  rec  hash_and_normalize_obj  sharer  o  = 
 
			
		
	
		
			
				
					    if  Obj . is_int  o  then  hash_and_normalize_int  o  else  hash_and_normalize_block  sharer  o 
 
			
		
	
		
			
				
					  * ) 
 
			
		
	
		
			
				
					  let  rec  hash_and_normalize_obj  sharer  o  parent_shallow_hash_block  parent_field_i  = 
 
			
		
	
		
			
				
					    if  Obj . is_int  o  then  hash_and_normalize_int  o 
 
			
		
	
		
			
				
					    else  hash_and_normalize_block  sharer  o  parent_shallow_hash_block  parent_field_i 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  and  hash_and_normalize_block  sharer  block  = 
 
			
		
	
		
			
				
					  and  hash_and_normalize_block  sharer  block  parent_shallow_hash_block  parent_field_i  = 
 
			
		
	
		
			
				
					    let  shallow_hash  =  Hashing . shallow  block  in 
 
			
		
	
		
			
				
					    let  shallow_hash_block  =  ( shallow_hash ,  block )  in 
 
			
		
	
		
			
				
					    let  tag  =  Obj . tag  block  in 
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -148,13 +157,25 @@ end = struct
 
			
		
	
		
			
				
					      |  Some  ( Normalized  hash_normalized )  -> 
 
			
		
	
		
			
				
					          (*  The block has already been visited, we can reuse the result.  *) 
 
			
		
	
		
			
				
					          hash_normalized 
 
			
		
	
		
			
				
					      |  Some  Visiting  -> 
 
			
		
	
		
			
				
					      |  Some  ( Visiting  visiting )   -> 
 
			
		
	
		
			
				
					          (* 
 
			
		
	
		
			
				
					            The  block  is  being  visited ,  which  means  we  have  a  cycle . 
 
			
		
	
		
			
				
					            We  record  fields  to  be  patched  after  we  have  finished  treating  the  cycle . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					            For  termination  we  have  to  return  a  shallow  hash . 
 
			
		
	
		
			
				
					            We  also  need  to  return  a  phys_equally  different  value  so  that  it  will  trigger 
 
			
		
	
		
			
				
					            copy - on - write  on  the  whole  cycle  ( then  patch  can  safely  be  applied  and  in  any  order ) , 
 
			
		
	
		
			
				
					            even  though  it  may  not  be  necessary  if  the  whole  cycle  and  its  dependencies  could  be 
 
			
		
	
		
			
				
					            kept  as - is . 
 
			
		
	
		
			
				
					            The  value  that  is  returned  should  not  be  hashed  or  used .   The  current  implementation 
 
			
		
	
		
			
				
					            respects  it . 
 
			
		
	
		
			
				
					          * ) 
 
			
		
	
		
			
				
					          raise  MaximumSharingCyclicValue 
 
			
		
	
		
			
				
					          visiting . to_patch  <-  ( parent_shallow_hash_block ,  parent_field_i )  ::  visiting . to_patch  ; 
 
			
		
	
		
			
				
					          ( shallow_hash ,  dummy_should_not_be_hashed_or_used ) 
 
			
		
	
		
			
				
					      |  None  -> 
 
			
		
	
		
			
				
					          HPhysEq . add  sharer . visited_blocks  shallow_hash_block  Visiting  ; 
 
			
		
	
		
			
				
					          let  visited  =  Visiting  { to_patch =  [] }  in 
 
			
		
	
		
			
				
					          let [ @ warning  " -8 " ]  ( Visiting  visiting )  =  visited  in 
 
			
		
	
		
			
				
					          HPhysEq . add  sharer . visited_blocks  shallow_hash_block  visited  ; 
 
			
		
	
		
			
				
					          let  hash_normalized  = 
 
			
		
	
		
			
				
					            if  Int . equal  tag  Obj . forward_tag  then  ( 
 
			
		
	
		
			
				
					              assert  ( not  sharer . fail_on_forward )  ; 
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -166,13 +187,14 @@ end = struct
 
			
		
	
		
			
				
					                This  remains  untested  for  now  ( hence  the  assertion  above ) . 
 
			
		
	
		
			
				
					                Not  obvious  to  test  as  optimizations  or  the  GC  can  already  do  the  substitution . 
 
			
		
	
		
			
				
					              * ) 
 
			
		
	
		
			
				
					              hash_and_normalize_obj  sharer  ( Obj . field  block  0 )  ) 
 
			
		
	
		
			
				
					              hash_and_normalize_obj  sharer  ( Obj . field  block  0 )  parent_shallow_hash_block 
 
			
		
	
		
			
				
					                parent_field_i  ) 
 
			
		
	
		
			
				
					            else  ( 
 
			
		
	
		
			
				
					              (*  For regular blocks, normalize each field then use a shallow comparison.  *) 
 
			
		
	
		
			
				
					              assert  ( ( not  sharer . fail_on_objects )  | |  not  ( Int . equal  tag  Obj . object_tag ) )  ; 
 
			
		
	
		
			
				
					              let  hash_shallow_normalized  = 
 
			
		
	
		
			
				
					                let  size  =  Obj . size  block  in 
 
			
		
	
		
			
				
					                hash_and_normalize_block_fields  sharer  block  size  0 
 
			
		
	
		
			
				
					                hash_and_normalize_block_fields  sharer  shallow_hash_block  block block  size  0 
 
			
		
	
		
			
				
					                  ( Hashing . alloc_of_block  ~ tag  ~ size ) 
 
			
		
	
		
			
				
					              in 
 
			
		
	
		
			
				
					              match  HNorm . find_opt  sharer . hash_normalized  hash_shallow_normalized  with 
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -182,15 +204,47 @@ end = struct
 
			
		
	
		
			
				
					                  HNorm . add  sharer . hash_normalized  hash_shallow_normalized  hash_shallow_normalized  ; 
 
			
		
	
		
			
				
					                  hash_shallow_normalized  ) 
 
			
		
	
		
			
				
					          in 
 
			
		
	
		
			
				
					          let  hash_normalized  = 
 
			
		
	
		
			
				
					            match  visiting . to_patch  with 
 
			
		
	
		
			
				
					            |  []  (*  not the head of a cycle  *)  -> 
 
			
		
	
		
			
				
					                hash_normalized 
 
			
		
	
		
			
				
					            |  _  ::  _  as  to_patch  -> 
 
			
		
	
		
			
				
					                (* 
 
			
		
	
		
			
				
					                  The  whole  cycle  has  been  treated ,  we  now  need  to  patch  values  that  pointed  to 
 
			
		
	
		
			
				
					                  this  block .   We  need  to  look  them  up  in  the  [ visited_blocks ]  hash  table  because 
 
			
		
	
		
			
				
					                  they  have  been  duplicated  since  we  recorded  them . 
 
			
		
	
		
			
				
					                * ) 
 
			
		
	
		
			
				
					                let  _ ,  normalized  =  hash_normalized  in 
 
			
		
	
		
			
				
					                List . iter  to_patch  ~ f : ( fun  ( hash_block_to_patch ,  field_i_to_patch )  -> 
 
			
		
	
		
			
				
					                    let  normalized_block_to_patch  = 
 
			
		
	
		
			
				
					                      if  phys_equal  hash_block_to_patch  shallow_hash_block  then 
 
			
		
	
		
			
				
					                        (*  Self-cycle, e.g. [let rec x = 1 :: x]. No lookup!  *) 
 
			
		
	
		
			
				
					                        normalized 
 
			
		
	
		
			
				
					                      else 
 
			
		
	
		
			
				
					                        let [ @ warning  " -8 " ]  ( Normalized  ( _ ,  normalized_block_to_patch ) )  = 
 
			
		
	
		
			
				
					                          HPhysEq . find  sharer . visited_blocks  hash_block_to_patch 
 
			
		
	
		
			
				
					                        in 
 
			
		
	
		
			
				
					                        normalized_block_to_patch 
 
			
		
	
		
			
				
					                    in 
 
			
		
	
		
			
				
					                    Obj . set_field  normalized_block_to_patch  field_i_to_patch  normalized  )  ; 
 
			
		
	
		
			
				
					                (* 
 
			
		
	
		
			
				
					                  For  cycle  heads ,  for  consistency  with  the  [ Visiting ]  case  above  we  need  to 
 
			
		
	
		
			
				
					                  use  the  shallow  hash . 
 
			
		
	
		
			
				
					                * ) 
 
			
		
	
		
			
				
					                ( shallow_hash ,  normalized ) 
 
			
		
	
		
			
				
					          in 
 
			
		
	
		
			
				
					          HPhysEq . replace  sharer . visited_blocks  shallow_hash_block  ( Normalized  hash_normalized )  ; 
 
			
		
	
		
			
				
					          hash_normalized 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  and  hash_and_normalize_block_fields  sharer  original_block  new_block  size  field_i  hash_state  = 
 
			
		
	
		
			
				
					  and  hash_and_normalize_block_fields  sharer  original_shallow_hash_block  original_block  new_block 
 
			
		
	
		
			
				
					      size  field_i  hash_state  = 
 
			
		
	
		
			
				
					    if  field_i  > =  size  then  ( Hashing . get_hash_value  hash_state ,  new_block ) 
 
			
		
	
		
			
				
					    else 
 
			
		
	
		
			
				
					      let  field_v  =  Obj . field  original_block  field_i  in 
 
			
		
	
		
			
				
					      let  field_hash ,  field_v'  =  hash_and_normalize_obj  sharer  field_v  in 
 
			
		
	
		
			
				
					      let  field_hash ,  field_v'  = 
 
			
		
	
		
			
				
					        hash_and_normalize_obj  sharer  field_v  original_shallow_hash_block  field_i 
 
			
		
	
		
			
				
					      in 
 
			
		
	
		
			
				
					      let  hash_state  =  Hashing . fold_hash_value  hash_state  field_hash  in 
 
			
		
	
		
			
				
					      let  new_block  = 
 
			
		
	
		
			
				
					        if  phys_equal  field_v  field_v'  then  new_block 
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -204,8 +258,12 @@ end = struct
 
			
		
	
		
			
				
					          Obj . set_field  new_block  field_i  field_v'  ; 
 
			
		
	
		
			
				
					          new_block 
 
			
		
	
		
			
				
					      in 
 
			
		
	
		
			
				
					      ( hash_and_normalize_block_fields  [ @ tailcall ] )  sharer  original_block  new_block  size 
 
			
		
	
		
			
				
					        ( field_i  +  1 )  hash_state 
 
			
		
	
		
			
				
					      ( hash_and_normalize_block_fields  [ @ tailcall ] )  sharer  original_shallow_hash_block 
 
			
		
	
		
			
				
					        original_block  new_block  size  ( field_i  +  1 )  hash_state 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let  dummy_should_not_be_patched  = 
 
			
		
	
		
			
				
					    ( Hashing . of_int  0 ,  (*  Make sure it fails hard if [Obj.set_field] is called on it  *)  Obj . repr  0 ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  (* * 
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -214,7 +272,8 @@ end = struct
 
			
		
	
		
			
				
					    Preserves  polymorphic  compare ,  hashing ,  no - sharing  marshalling . 
 
			
		
	
		
			
				
					    May  have  an  impact  on  code  using  [ phys_equal ]  or  marshalling  with  sharing . 
 
			
		
	
		
			
				
					  * ) 
 
			
		
	
		
			
				
					  let  normalize_value  sharer  v  =  hash_and_normalize_obj  sharer  ( Obj . repr  v )  | >  snd  | >  Obj . obj 
 
			
		
	
		
			
				
					  let  normalize_value  sharer  v  = 
 
			
		
	
		
			
				
					    hash_and_normalize_obj  sharer  ( Obj . repr  v )  dummy_should_not_be_patched  ( - 1 )  | >  snd  | >  Obj . obj 
 
			
		
	
		
			
				
					end  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					module  ForHashtbl  ( H  :  Caml . Hashtbl . S )  =  struct