@@ -130,6 +130,60 @@ impl<'tcx> GotocCtx<'tcx> {
130
130
} } ;
131
131
}
132
132
133
+ // Intrinsics which encode a simple arithmetic operation with overflow check
134
+ macro_rules! codegen_op_with_overflow_check {
135
+ ( $f: ident) => { {
136
+ let a = fargs. remove( 0 ) ;
137
+ let b = fargs. remove( 0 ) ;
138
+ let res = a. $f( b) ;
139
+ let check = Stmt :: assert(
140
+ res. overflowed. not( ) ,
141
+ format!( "attempt to compute {} which would overflow" , intrinsic) . as_str( ) ,
142
+ loc,
143
+ ) ;
144
+ let expr_place = self . codegen_expr_to_place( p, res. result) ;
145
+ Stmt :: block( vec![ expr_place, check] , loc)
146
+ } } ;
147
+ }
148
+
149
+ // Intrinsics which encode a SIMD arithmetic operation with overflow check.
150
+ // We expand the overflow check because CBMC overflow operations don't accept array as
151
+ // argument.
152
+ macro_rules! codegen_simd_with_overflow_check {
153
+ ( $op: ident, $overflow: ident) => { {
154
+ let a = fargs. remove( 0 ) ;
155
+ let b = fargs. remove( 0 ) ;
156
+ let mut check = Expr :: bool_false( ) ;
157
+ if let Type :: Vector { size, .. } = a. typ( ) {
158
+ let a_size = size;
159
+ if let Type :: Vector { size, .. } = b. typ( ) {
160
+ let b_size = size;
161
+ assert_eq!( a_size, b_size, "Expected same length vectors" , ) ;
162
+ for i in 0 ..* a_size {
163
+ // create expression
164
+ let index = Expr :: int_constant( i, Type :: ssize_t( ) ) ;
165
+ let v_a = a. clone( ) . index_array( index. clone( ) ) ;
166
+ let v_b = b. clone( ) . index_array( index) ;
167
+ check = check. or( v_a. $overflow( v_b) ) ;
168
+ }
169
+ }
170
+ }
171
+ let check_stmt = Stmt :: assert(
172
+ check. not( ) ,
173
+ format!( "attempt to compute {} which would overflow" , intrinsic) . as_str( ) ,
174
+ loc,
175
+ ) ;
176
+ let res = a. $op( b) ;
177
+ let expr_place = self . codegen_expr_to_place( p, res) ;
178
+ Stmt :: block( vec![ expr_place, check_stmt] , loc)
179
+ } } ;
180
+ }
181
+
182
+ // Intrinsics which encode a simple wrapping arithmetic operation
183
+ macro_rules! codegen_wrapping_op {
184
+ ( $f: ident) => { { codegen_intrinsic_binop!( $f) } } ;
185
+ }
186
+
133
187
// Intrinsics which encode a simple binary operation
134
188
macro_rules! codegen_intrinsic_boolean_binop {
135
189
( $f: ident) => { { self . binop( p, fargs, |a, b| a. $f( b) . cast_to( Type :: c_bool( ) ) ) } } ;
@@ -203,6 +257,7 @@ impl<'tcx> GotocCtx<'tcx> {
203
257
// *var1 = op(*var1, var2);
204
258
// var = tmp;
205
259
// -------------------------
260
+ // Note: Atomic arithmetic operations wrap around on overflow.
206
261
macro_rules! codegen_atomic_binop {
207
262
( $op: ident) => { {
208
263
warn!( "RMC does not support concurrency for now. {} treated as a sequential operation." , intrinsic) ;
@@ -227,7 +282,7 @@ impl<'tcx> GotocCtx<'tcx> {
227
282
match intrinsic {
228
283
"abort" => Stmt :: assert_false ( "abort intrinsic" , loc) ,
229
284
"add_with_overflow" => codegen_op_with_overflow ! ( add_overflow) ,
230
- "arith_offset" => codegen_intrinsic_binop ! ( plus) ,
285
+ "arith_offset" => codegen_wrapping_op ! ( plus) ,
231
286
"assert_inhabited" => {
232
287
let ty = instance. substs . type_at ( 0 ) ;
233
288
let layout = self . layout_of ( ty) ;
@@ -356,7 +411,7 @@ impl<'tcx> GotocCtx<'tcx> {
356
411
"nearbyintf32" => codegen_simple_intrinsic ! ( Nearbyintf ) ,
357
412
"nearbyintf64" => codegen_simple_intrinsic ! ( Nearbyint ) ,
358
413
"needs_drop" => codegen_intrinsic_const ! ( ) ,
359
- "offset" => codegen_intrinsic_binop ! ( plus ) ,
414
+ "offset" => codegen_op_with_overflow_check ! ( add_overflow ) ,
360
415
"powf32" => codegen_simple_intrinsic ! ( Powf ) ,
361
416
"powf64" => codegen_simple_intrinsic ! ( Pow ) ,
362
417
"powif32" => codegen_simple_intrinsic ! ( Powif ) ,
@@ -376,7 +431,7 @@ impl<'tcx> GotocCtx<'tcx> {
376
431
"saturating_sub" => codegen_intrinsic_binop_with_mm ! ( saturating_sub) ,
377
432
"sinf32" => codegen_simple_intrinsic ! ( Sinf ) ,
378
433
"sinf64" => codegen_simple_intrinsic ! ( Sin ) ,
379
- "simd_add" => codegen_intrinsic_binop ! ( plus) ,
434
+ "simd_add" => codegen_simd_with_overflow_check ! ( plus, add_overflow_p ) ,
380
435
"simd_and" => codegen_intrinsic_binop ! ( bitand) ,
381
436
"simd_div" => codegen_intrinsic_binop ! ( div) ,
382
437
"simd_eq" => codegen_intrinsic_binop ! ( eq) ,
@@ -390,7 +445,7 @@ impl<'tcx> GotocCtx<'tcx> {
390
445
"simd_insert" => self . codegen_intrinsic_simd_insert ( fargs, p, cbmc_ret_ty, loc) ,
391
446
"simd_le" => codegen_intrinsic_binop ! ( le) ,
392
447
"simd_lt" => codegen_intrinsic_binop ! ( lt) ,
393
- "simd_mul" => codegen_intrinsic_binop ! ( mul) ,
448
+ "simd_mul" => codegen_simd_with_overflow_check ! ( mul, mul_overflow_p ) ,
394
449
"simd_ne" => codegen_intrinsic_binop ! ( neq) ,
395
450
"simd_or" => codegen_intrinsic_binop ! ( bitor) ,
396
451
"simd_rem" => codegen_intrinsic_binop ! ( rem) ,
@@ -404,7 +459,7 @@ impl<'tcx> GotocCtx<'tcx> {
404
459
}
405
460
}
406
461
// "simd_shuffle#" => handled in an `if` preceding this match
407
- "simd_sub" => codegen_intrinsic_binop ! ( sub) ,
462
+ "simd_sub" => codegen_simd_with_overflow_check ! ( sub, sub_overflow_p ) ,
408
463
"simd_xor" => codegen_intrinsic_binop ! ( bitxor) ,
409
464
"size_of" => codegen_intrinsic_const ! ( ) ,
410
465
"size_of_val" => codegen_size_align ! ( size) ,
@@ -422,9 +477,9 @@ impl<'tcx> GotocCtx<'tcx> {
422
477
"unaligned_volatile_load" => {
423
478
self . codegen_expr_to_place ( p, fargs. remove ( 0 ) . dereference ( ) )
424
479
}
425
- "unchecked_add" => codegen_intrinsic_binop ! ( plus ) ,
480
+ "unchecked_add" => codegen_op_with_overflow_check ! ( add_overflow ) ,
426
481
"unchecked_div" => codegen_intrinsic_binop ! ( div) ,
427
- "unchecked_mul" => codegen_intrinsic_binop ! ( mul ) ,
482
+ "unchecked_mul" => codegen_op_with_overflow_check ! ( mul_overflow ) ,
428
483
"unchecked_rem" => codegen_intrinsic_binop ! ( rem) ,
429
484
"unchecked_shl" => codegen_intrinsic_binop ! ( shl) ,
430
485
"unchecked_shr" => {
@@ -434,15 +489,15 @@ impl<'tcx> GotocCtx<'tcx> {
434
489
codegen_intrinsic_binop ! ( lshr)
435
490
}
436
491
}
437
- "unchecked_sub" => codegen_intrinsic_binop ! ( sub ) ,
492
+ "unchecked_sub" => codegen_op_with_overflow_check ! ( sub_overflow ) ,
438
493
"unlikely" => self . codegen_expr_to_place ( p, fargs. remove ( 0 ) ) ,
439
494
"unreachable" => Stmt :: assert_false ( "unreachable" , loc) ,
440
495
"volatile_copy_memory" => codegen_intrinsic_copy ! ( Memmove ) ,
441
496
"volatile_copy_nonoverlapping_memory" => codegen_intrinsic_copy ! ( Memcpy ) ,
442
497
"volatile_load" => self . codegen_expr_to_place ( p, fargs. remove ( 0 ) . dereference ( ) ) ,
443
- "wrapping_add" => codegen_intrinsic_binop ! ( plus) ,
444
- "wrapping_mul" => codegen_intrinsic_binop ! ( mul) ,
445
- "wrapping_sub" => codegen_intrinsic_binop ! ( sub) ,
498
+ "wrapping_add" => codegen_wrapping_op ! ( plus) ,
499
+ "wrapping_mul" => codegen_wrapping_op ! ( mul) ,
500
+ "wrapping_sub" => codegen_wrapping_op ! ( sub) ,
446
501
"write_bytes" => {
447
502
let dst = fargs. remove ( 0 ) . cast_to ( Type :: void_pointer ( ) ) ;
448
503
let val = fargs. remove ( 0 ) . cast_to ( Type :: c_int ( ) ) ;
0 commit comments