@@ -146,39 +146,6 @@ impl<'tcx> GotocCtx<'tcx> {
146
146
let farg_types = sig. inputs ( ) ;
147
147
let cbmc_ret_ty = self . codegen_ty ( ret_ty) ;
148
148
149
- /// https://doc.rust-lang.org/core/intrinsics/fn.copy.html
150
- /// https://doc.rust-lang.org/core/intrinsics/fn.copy_nonoverlapping.html
151
- /// An intrinsic that translates directly into either memmove (for copy) or memcpy (copy_nonoverlapping)
152
- macro_rules! _codegen_intrinsic_copy {
153
- ( $f: ident) => { {
154
- let src = fargs. remove( 0 ) . cast_to( Type :: void_pointer( ) ) ;
155
- let dst = fargs. remove( 0 ) . cast_to( Type :: void_pointer( ) ) ;
156
- let count = fargs. remove( 0 ) ;
157
- let sz = {
158
- match self . fn_sig_of_instance( instance) . unwrap( ) . skip_binder( ) . inputs( ) [ 0 ]
159
- . kind( )
160
- {
161
- ty:: RawPtr ( t) => {
162
- let layout = self . layout_of( t. ty) ;
163
- Expr :: int_constant( layout. size. bytes( ) , Type :: size_t( ) )
164
- }
165
- _ => unreachable!( ) ,
166
- }
167
- } ;
168
- let n = sz. mul( count) ;
169
- let call_memcopy = BuiltinFn :: $f. call( vec![ dst. clone( ) , src, n. clone( ) ] , loc) ;
170
-
171
- // The C implementation of memcpy does not allow an invalid pointer for
172
- // the src/dst, but the LLVM implementation specifies that a copy with
173
- // length zero is a no-op. This comes up specifically when handling
174
- // the empty string; CBMC will fail on passing a reference to empty
175
- // string unless we codegen this zero check.
176
- // https://llvm.org/docs/LangRef.html#llvm-memcpy-intrinsic
177
- let copy_if_nontrivial = n. is_zero( ) . ternary( dst, call_memcopy) ;
178
- self . codegen_expr_to_place( p, copy_if_nontrivial)
179
- } } ;
180
- }
181
-
182
149
// Codegens a simple intrinsic: ie. one which maps directly to a matching goto construct
183
150
// We need to use this macro form because of a known limitation in rust
184
151
// `codegen_simple_intrinsic!(self.get_sqrt(), Type::float())` gives the error message:
@@ -500,8 +467,10 @@ impl<'tcx> GotocCtx<'tcx> {
500
467
"ceilf64" => codegen_unimplemented_intrinsic ! (
501
468
"https://github.com/model-checking/kani/issues/1025"
502
469
) ,
503
- "copy" => unstable_codegen ! ( _codegen_intrinsic_copy!( Memmove ) ) ,
504
- "copy_nonoverlapping" => unstable_codegen ! ( _codegen_intrinsic_copy!( Memcpy ) ) ,
470
+ "copy" => self . codegen_copy_intrinsic ( intrinsic, fargs, farg_types, p, loc) ,
471
+ "copy_nonoverlapping" => unreachable ! (
472
+ "Expected `core::intrinsics::unreachable` to be handled by `StatementKind::CopyNonOverlapping`"
473
+ ) ,
505
474
"copysignf32" => unstable_codegen ! ( codegen_simple_intrinsic!( Copysignf ) ) ,
506
475
"copysignf64" => unstable_codegen ! ( codegen_simple_intrinsic!( Copysign ) ) ,
507
476
"cosf32" => codegen_simple_intrinsic ! ( Cosf ) ,
@@ -928,6 +897,65 @@ impl<'tcx> GotocCtx<'tcx> {
928
897
Stmt :: atomic_block ( vec ! [ skip_stmt] , loc)
929
898
}
930
899
900
+ /// An intrinsic that translates directly into `memmove`
901
+ /// https://doc.rust-lang.org/core/intrinsics/fn.copy.html
902
+ ///
903
+ /// Undefined behavior if any of these conditions are violated:
904
+ /// * Both `src`/`dst` must be properly aligned (done by alignment checks)
905
+ /// * Both `src`/`dst` must be valid for reads/writes of `count *
906
+ /// size_of::<T>()` bytes (done by calls to `memmove`)
907
+ /// In addition, we check that computing `count` in bytes (i.e., the third
908
+ /// argument for the `memmove` call) would not overflow.
909
+ fn codegen_copy_intrinsic (
910
+ & mut self ,
911
+ intrinsic : & str ,
912
+ mut fargs : Vec < Expr > ,
913
+ farg_types : & [ Ty < ' tcx > ] ,
914
+ p : & Place < ' tcx > ,
915
+ loc : Location ,
916
+ ) -> Stmt {
917
+ // The two first arguments are pointers. It's safe to cast them to void
918
+ // pointers or directly unwrap the `pointee_type` result as seen later.
919
+ let src = fargs. remove ( 0 ) . cast_to ( Type :: void_pointer ( ) ) ;
920
+ let dst = fargs. remove ( 0 ) . cast_to ( Type :: void_pointer ( ) ) ;
921
+
922
+ // Generate alignment checks for both pointers
923
+ let src_align = self . is_ptr_aligned ( farg_types[ 0 ] , src. clone ( ) ) ;
924
+ let src_align_check = self . codegen_assert (
925
+ src_align,
926
+ PropertyClass :: DefaultAssertion ,
927
+ "`src` is properly aligned" ,
928
+ loc,
929
+ ) ;
930
+ let dst_align = self . is_ptr_aligned ( farg_types[ 1 ] , dst. clone ( ) ) ;
931
+ let dst_align_check = self . codegen_assert (
932
+ dst_align,
933
+ PropertyClass :: DefaultAssertion ,
934
+ "`dst` is properly aligned" ,
935
+ loc,
936
+ ) ;
937
+
938
+ // Compute the number of bytes to be copied
939
+ let count = fargs. remove ( 0 ) ;
940
+ let pointee_type = pointee_type ( farg_types[ 0 ] ) . unwrap ( ) ;
941
+ let ( count_bytes, overflow_check) =
942
+ self . count_in_bytes ( count, pointee_type, Type :: size_t ( ) , intrinsic, loc) ;
943
+
944
+ // Build the call to `memmove`
945
+ let call_memmove =
946
+ BuiltinFn :: Memmove . call ( vec ! [ dst. clone( ) , src, count_bytes. clone( ) ] , loc) ;
947
+
948
+ // The C implementation of `memmove` does not allow an invalid pointer for
949
+ // `src` nor `dst`, but the LLVM implementation specifies that a copy with
950
+ // length zero is a no-op. This comes up specifically when handling
951
+ // the empty string; CBMC will fail on passing a reference to empty
952
+ // string unless we codegen this zero check.
953
+ // https://llvm.org/docs/LangRef.html#llvm-memmove-intrinsic
954
+ let copy_if_nontrivial = count_bytes. is_zero ( ) . ternary ( dst, call_memmove) ;
955
+ let copy_expr = self . codegen_expr_to_place ( p, copy_if_nontrivial) ;
956
+ Stmt :: block ( vec ! [ src_align_check, dst_align_check, overflow_check, copy_expr] , loc)
957
+ }
958
+
931
959
/// Computes the offset from a pointer
932
960
/// https://doc.rust-lang.org/std/intrinsics/fn.offset.html
933
961
fn codegen_offset (
0 commit comments