@@ -599,18 +599,47 @@ where
599
599
}
600
600
601
601
/// Write a value to a place
602
+ #[ inline( always) ]
602
603
pub fn write_value (
603
604
& mut self ,
604
605
src_val : Value < M :: PointerTag > ,
605
606
dest : PlaceTy < ' tcx , M :: PointerTag > ,
606
607
) -> EvalResult < ' tcx > {
607
- trace ! ( "write_value: {:?} <- {:?}" , * dest, src_val ) ;
608
- // Check that the value actually is okay for that type
608
+ self . write_value_no_validate ( src_val , dest) ? ;
609
+
609
610
if M :: ENFORCE_VALIDITY {
610
- // Something changed somewhere, better make sure it matches the type!
611
- let op = OpTy { op : Operand :: Immediate ( src_val) , layout : dest. layout } ;
612
- self . validate_operand ( op, & mut vec ! [ ] , None , /*const_mode*/ false ) ?;
611
+ // Data got changed, better make sure it matches the type!
612
+ self . validate_operand ( self . place_to_op ( dest) ?, & mut vec ! [ ] , None , /*const_mode*/ false ) ?;
613
+ }
614
+
615
+ Ok ( ( ) )
616
+ }
617
+
618
+ /// Write a value to a place.
619
+ /// If you use this you are responsible for validating that things got copied at the
620
+ /// right type.
621
+ fn write_value_no_validate (
622
+ & mut self ,
623
+ src_val : Value < M :: PointerTag > ,
624
+ dest : PlaceTy < ' tcx , M :: PointerTag > ,
625
+ ) -> EvalResult < ' tcx > {
626
+ if cfg ! ( debug_assertions) {
627
+ // This is a very common path, avoid some checks in release mode
628
+ assert ! ( !dest. layout. is_unsized( ) , "Cannot write unsized data" ) ;
629
+ match src_val {
630
+ Value :: Scalar ( ScalarMaybeUndef :: Scalar ( Scalar :: Ptr ( _) ) ) =>
631
+ assert_eq ! ( self . pointer_size( ) , dest. layout. size,
632
+ "Size mismatch when writing pointer" ) ,
633
+ Value :: Scalar ( ScalarMaybeUndef :: Scalar ( Scalar :: Bits { size, .. } ) ) =>
634
+ assert_eq ! ( Size :: from_bytes( size. into( ) ) , dest. layout. size,
635
+ "Size mismatch when writing bits" ) ,
636
+ Value :: Scalar ( ScalarMaybeUndef :: Undef ) => { } , // undef can have any size
637
+ Value :: ScalarPair ( _, _) => {
638
+ // FIXME: Can we check anything here?
639
+ }
640
+ }
613
641
}
642
+ trace ! ( "write_value: {:?} <- {:?}: {}" , * dest, src_val, dest. layout. ty) ;
614
643
615
644
// See if we can avoid an allocation. This is the counterpart to `try_read_value`,
616
645
// but not factored as a separate function.
@@ -627,15 +656,16 @@ where
627
656
} ,
628
657
Place :: Ptr ( mplace) => mplace, // already in memory
629
658
} ;
659
+ let dest = MPlaceTy { mplace, layout : dest. layout } ;
630
660
631
661
// This is already in memory, write there.
632
- let dest = MPlaceTy { mplace, layout : dest. layout } ;
633
- self . write_value_to_mplace ( src_val, dest)
662
+ self . write_value_to_mplace_no_validate ( src_val, dest)
634
663
}
635
664
636
- /// Write a value to memory. This does NOT do validation, so you better had already
637
- /// done that before calling this!
638
- fn write_value_to_mplace (
665
+ /// Write a value to memory.
666
+ /// If you use this you are responsible for validating that things git copied at the
667
+ /// right type.
668
+ fn write_value_to_mplace_no_validate (
639
669
& mut self ,
640
670
value : Value < M :: PointerTag > ,
641
671
dest : MPlaceTy < ' tcx , M :: PointerTag > ,
@@ -653,8 +683,17 @@ where
653
683
}
654
684
655
685
let ptr = ptr. to_ptr ( ) ?;
686
+ // FIXME: We should check that there are dest.layout.size many bytes available in
687
+ // memory. The code below is not sufficient, with enough padding it might not
688
+ // cover all the bytes!
656
689
match value {
657
690
Value :: Scalar ( scalar) => {
691
+ match dest. layout . abi {
692
+ layout:: Abi :: Scalar ( _) => { } , // fine
693
+ _ => bug ! ( "write_value_to_mplace: invalid Scalar layout: {:#?}" ,
694
+ dest. layout)
695
+ }
696
+
658
697
self . memory . write_scalar (
659
698
ptr, ptr_align. min ( dest. layout . align ) , scalar, dest. layout . size
660
699
)
@@ -670,45 +709,109 @@ where
670
709
let b_offset = a_size. abi_align ( b_align) ;
671
710
let b_ptr = ptr. offset ( b_offset, & self ) ?. into ( ) ;
672
711
712
+ // It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
713
+ // but that does not work: We could be a newtype around a pair, then the
714
+ // fields do not match the `ScalarPair` components.
715
+
673
716
self . memory . write_scalar ( ptr, ptr_align. min ( a_align) , a_val, a_size) ?;
674
717
self . memory . write_scalar ( b_ptr, ptr_align. min ( b_align) , b_val, b_size)
675
718
}
676
719
}
677
720
}
678
721
679
- /// Copy the data from an operand to a place
722
+ /// Copy the data from an operand to a place. This does not support transmuting!
723
+ /// Use `copy_op_transmute` if the layouts could disagree.
724
+ #[ inline( always) ]
680
725
pub fn copy_op (
681
726
& mut self ,
682
727
src : OpTy < ' tcx , M :: PointerTag > ,
683
728
dest : PlaceTy < ' tcx , M :: PointerTag > ,
684
729
) -> EvalResult < ' tcx > {
685
- assert ! ( !src. layout. is_unsized( ) && !dest. layout. is_unsized( ) ,
730
+ self . copy_op_no_validate ( src, dest) ?;
731
+
732
+ if M :: ENFORCE_VALIDITY {
733
+ // Data got changed, better make sure it matches the type!
734
+ self . validate_operand ( self . place_to_op ( dest) ?, & mut vec ! [ ] , None , /*const_mode*/ false ) ?;
735
+ }
736
+
737
+ Ok ( ( ) )
738
+ }
739
+
740
+ /// Copy the data from an operand to a place. This does not support transmuting!
741
+ /// Use `copy_op_transmute` if the layouts could disagree.
742
+ /// Also, if you use this you are responsible for validating that things git copied at the
743
+ /// right type.
744
+ fn copy_op_no_validate (
745
+ & mut self ,
746
+ src : OpTy < ' tcx , M :: PointerTag > ,
747
+ dest : PlaceTy < ' tcx , M :: PointerTag > ,
748
+ ) -> EvalResult < ' tcx > {
749
+ debug_assert ! ( !src. layout. is_unsized( ) && !dest. layout. is_unsized( ) ,
686
750
"Cannot copy unsized data" ) ;
687
- assert_eq ! ( src. layout. size, dest. layout. size,
688
- "Size mismatch when copying!\n src: {:#?}\n dest: {:#?}" , src, dest) ;
751
+ // We do NOT compare the types for equality, because well-typed code can
752
+ // actually "transmute" `&mut T` to `&T` in an assignment without a cast.
753
+ assert ! ( src. layout. details == dest. layout. details,
754
+ "Layout mismatch when copying!\n src: {:#?}\n dest: {:#?}" , src, dest) ;
689
755
690
756
// Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
691
- let ( src_ptr, src_align) = match self . try_read_value ( src) ? {
692
- Ok ( src_val) =>
693
- // Yay, we got a value that we can write directly. We write with the
694
- // *source layout*, because that was used to load, and if they do not match
695
- // this is a transmute we want to support.
696
- return self . write_value ( src_val, PlaceTy { place : * dest, layout : src. layout } ) ,
697
- Err ( mplace) => mplace. to_scalar_ptr_align ( ) ,
757
+ let src = match self . try_read_value ( src) ? {
758
+ Ok ( src_val) => {
759
+ // Yay, we got a value that we can write directly.
760
+ return self . write_value_no_validate ( src_val, dest) ;
761
+ }
762
+ Err ( mplace) => mplace,
698
763
} ;
699
764
// Slow path, this does not fit into an immediate. Just memcpy.
700
- trace ! ( "copy_op: {:?} <- {:?}" , * dest, * src) ;
765
+ trace ! ( "copy_op: {:?} <- {:?}: {}" , * dest, src, dest. layout. ty) ;
766
+
701
767
let dest = self . force_allocation ( dest) ?;
768
+ let ( src_ptr, src_align) = src. to_scalar_ptr_align ( ) ;
702
769
let ( dest_ptr, dest_align) = dest. to_scalar_ptr_align ( ) ;
703
770
self . memory . copy (
704
771
src_ptr, src_align,
705
772
dest_ptr, dest_align,
706
- src . layout . size , false
773
+ dest . layout . size , false
707
774
) ?;
775
+
776
+ Ok ( ( ) )
777
+ }
778
+
779
+ /// Copy the data from an operand to a place. The layouts may disagree, but they must
780
+ /// have the same size.
781
+ pub fn copy_op_transmute (
782
+ & mut self ,
783
+ src : OpTy < ' tcx , M :: PointerTag > ,
784
+ dest : PlaceTy < ' tcx , M :: PointerTag > ,
785
+ ) -> EvalResult < ' tcx > {
786
+ if src. layout . details == dest. layout . details {
787
+ // Fast path: Just use normal `copy_op`
788
+ return self . copy_op ( src, dest) ;
789
+ }
790
+ // We still require the sizes to match
791
+ debug_assert ! ( !src. layout. is_unsized( ) && !dest. layout. is_unsized( ) ,
792
+ "Cannot copy unsized data" ) ;
793
+ assert ! ( src. layout. size == dest. layout. size,
794
+ "Size mismatch when transmuting!\n src: {:#?}\n dest: {:#?}" , src, dest) ;
795
+
796
+ // The hard case is `ScalarPair`. `src` is already read from memory in this case,
797
+ // using `src.layout` to figure out which bytes to use for the 1st and 2nd field.
798
+ // We have to write them to `dest` at the offsets they were *read at*, which is
799
+ // not necessarily the same as the offsets in `dest.layout`!
800
+ // Hence we do the copy with the source layout on both sides. We also make sure to write
801
+ // into memory, because if `dest` is a local we would not even have a way to write
802
+ // at the `src` offsets; the fact that we came from a different layout would
803
+ // just be lost.
804
+ let dest = self . force_allocation ( dest) ?;
805
+ self . copy_op_no_validate (
806
+ src,
807
+ PlaceTy :: from ( MPlaceTy { mplace : * dest, layout : src. layout } ) ,
808
+ ) ?;
809
+
708
810
if M :: ENFORCE_VALIDITY {
709
- // Something changed somewhere , better make sure it matches the type!
811
+ // Data got changed , better make sure it matches the type!
710
812
self . validate_operand ( dest. into ( ) , & mut vec ! [ ] , None , /*const_mode*/ false ) ?;
711
813
}
814
+
712
815
Ok ( ( ) )
713
816
}
714
817
@@ -734,7 +837,7 @@ where
734
837
let ptr = self . allocate ( local_layout, MemoryKind :: Stack ) ?;
735
838
// We don't have to validate as we can assume the local
736
839
// was already valid for its type.
737
- self . write_value_to_mplace ( value, ptr) ?;
840
+ self . write_value_to_mplace_no_validate ( value, ptr) ?;
738
841
let mplace = ptr. mplace ;
739
842
// Update the local
740
843
* self . stack [ frame] . locals [ local] . access_mut ( ) ? =
0 commit comments