Skip to content

Commit d7fd6ec

Browse files
committed
Use optimized overflow operation everywhere
This is related to the performance degradation we are seeing in the toolchain upgrade: model-checking#2293
1 parent b5c116d commit d7fd6ec

File tree

3 files changed

+97
-55
lines changed

3 files changed

+97
-55
lines changed

cprover_bindings/src/goto_program/expr.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,9 +1403,9 @@ impl Expr {
14031403
ArithmeticOverflowResult { result, overflowed }
14041404
}
14051405

1406-
/// Uses CBMC's add-with-overflow operation that performs a single addition
1406+
/// Uses CBMC's [binop]-with-overflow operation that performs a single addition
14071407
/// operation
1408-
/// `struct (T, bool) overflow(+, self, e)` where `T` is the type of `self`
1408+
/// `struct (T, bool) overflow(binop, self, e)` where `T` is the type of `self`
14091409
/// Pseudocode:
14101410
/// ```
14111411
/// struct overflow_result_t {
@@ -1417,6 +1417,14 @@ impl Expr {
14171417
/// overflow_result.overflowed = raw_result > maximum value of T;
14181418
/// return overflow_result;
14191419
/// ```
1420+
pub fn overflow_op(self, op: BinaryOperator, e: Expr) -> Expr {
1421+
assert!(
1422+
matches!(op, OverflowResultMinus | OverflowResultMult | OverflowResultPlus),
1423+
"Expected an overflow operation, but found: `{op:?}`"
1424+
);
1425+
self.binop(op, e)
1426+
}
1427+
14201428
pub fn add_overflow_result(self, e: Expr) -> Expr {
14211429
self.binop(OverflowResultPlus, e)
14221430
}
@@ -1448,6 +1456,7 @@ impl Expr {
14481456

14491457
/// `ArithmeticOverflowResult r; >>>r.overflowed = builtin_sub_overflow(self, e, &r.result)<<<`
14501458
pub fn mul_overflow(self, e: Expr) -> ArithmeticOverflowResult {
1459+
// TODO: Should we always use the one below?
14511460
let result = self.clone().mul(e.clone());
14521461
let overflowed = self.mul_overflow_p(e);
14531462
ArithmeticOverflowResult { result, overflowed }

kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use super::typ::{self, pointee_type};
55
use super::PropertyClass;
66
use crate::codegen_cprover_gotoc::GotocCtx;
77
use cbmc::goto_program::{
8-
arithmetic_overflow_result_type, ArithmeticOverflowResult, BuiltinFn, Expr, Location, Stmt,
9-
Type, ARITH_OVERFLOW_OVERFLOWED_FIELD, ARITH_OVERFLOW_RESULT_FIELD,
8+
arithmetic_overflow_result_type, ArithmeticOverflowResult, BinaryOperator, BuiltinFn, Expr,
9+
Location, Stmt, Type, ARITH_OVERFLOW_OVERFLOWED_FIELD, ARITH_OVERFLOW_RESULT_FIELD,
1010
};
1111
use rustc_middle::mir::{BasicBlock, Operand, Place};
1212
use rustc_middle::ty::layout::LayoutOf;
@@ -172,38 +172,6 @@ impl<'tcx> GotocCtx<'tcx> {
172172
}};
173173
}
174174

175-
// Intrinsics of the form *_with_overflow
176-
macro_rules! codegen_op_with_overflow {
177-
($f:ident) => {{
178-
let pt = self.place_ty(p);
179-
let t = self.codegen_ty(pt);
180-
let a = fargs.remove(0);
181-
let b = fargs.remove(0);
182-
let op_type = a.typ().clone();
183-
let res = a.$f(b);
184-
// add to symbol table
185-
let struct_tag = self.codegen_arithmetic_overflow_result_type(op_type.clone());
186-
assert_eq!(*res.typ(), struct_tag);
187-
188-
// store the result in a temporary variable
189-
let (var, decl) = self.decl_temp_variable(struct_tag, Some(res), loc);
190-
// cast into result type
191-
let e = Expr::struct_expr_from_values(
192-
t.clone(),
193-
vec![
194-
var.clone().member(ARITH_OVERFLOW_RESULT_FIELD, &self.symbol_table),
195-
var.member(ARITH_OVERFLOW_OVERFLOWED_FIELD, &self.symbol_table)
196-
.cast_to(Type::c_bool()),
197-
],
198-
&self.symbol_table,
199-
);
200-
self.codegen_expr_to_place(
201-
p,
202-
Expr::statement_expression(vec![decl, e.as_stmt(loc)], t),
203-
)
204-
}};
205-
}
206-
207175
// Intrinsics which encode a simple arithmetic operation with overflow check
208176
macro_rules! codegen_op_with_overflow_check {
209177
($f:ident) => {{
@@ -362,7 +330,9 @@ impl<'tcx> GotocCtx<'tcx> {
362330
}
363331

364332
match intrinsic {
365-
"add_with_overflow" => codegen_op_with_overflow!(add_overflow_result),
333+
"add_with_overflow" => {
334+
self.codegen_op_with_overflow(BinaryOperator::OverflowResultPlus, fargs, p, loc)
335+
}
366336
"arith_offset" => self.codegen_offset(intrinsic, instance, fargs, p, loc),
367337
"assert_inhabited" => self.codegen_assert_intrinsic(instance, intrinsic, span),
368338
"assert_mem_uninitialized_valid" => {
@@ -528,7 +498,9 @@ impl<'tcx> GotocCtx<'tcx> {
528498
"min_align_of_val" => codegen_size_align!(align),
529499
"minnumf32" => codegen_simple_intrinsic!(Fminf),
530500
"minnumf64" => codegen_simple_intrinsic!(Fmin),
531-
"mul_with_overflow" => codegen_op_with_overflow!(mul_overflow_result),
501+
"mul_with_overflow" => {
502+
self.codegen_op_with_overflow(BinaryOperator::OverflowResultMult, fargs, p, loc)
503+
}
532504
"nearbyintf32" => codegen_simple_intrinsic!(Nearbyintf),
533505
"nearbyintf64" => codegen_simple_intrinsic!(Nearbyint),
534506
"needs_drop" => codegen_intrinsic_const!(),
@@ -615,7 +587,9 @@ impl<'tcx> GotocCtx<'tcx> {
615587
"size_of_val" => codegen_size_align!(size),
616588
"sqrtf32" => unstable_codegen!(codegen_simple_intrinsic!(Sqrtf)),
617589
"sqrtf64" => unstable_codegen!(codegen_simple_intrinsic!(Sqrt)),
618-
"sub_with_overflow" => codegen_op_with_overflow!(sub_overflow_result),
590+
"sub_with_overflow" => {
591+
self.codegen_op_with_overflow(BinaryOperator::OverflowResultMinus, fargs, p, loc)
592+
}
619593
"transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, p),
620594
"truncf32" => codegen_simple_intrinsic!(Truncf),
621595
"truncf64" => codegen_simple_intrinsic!(Trunc),
@@ -719,6 +693,25 @@ impl<'tcx> GotocCtx<'tcx> {
719693
dividend_is_int_min.and(divisor_is_minus_one).not()
720694
}
721695

696+
/// Intrinsics of the form *_with_overflow
697+
fn codegen_op_with_overflow(
698+
&mut self,
699+
binop: BinaryOperator,
700+
mut fargs: Vec<Expr>,
701+
place: &Place<'tcx>,
702+
loc: Location,
703+
) -> Stmt {
704+
let place_ty = self.place_ty(place);
705+
let result_type = self.codegen_ty(place_ty);
706+
let left = fargs.remove(0);
707+
let right = fargs.remove(0);
708+
let res = self.codegen_binop_with_overflow(binop, left, right, result_type.clone(), loc);
709+
self.codegen_expr_to_place(
710+
place,
711+
Expr::statement_expression(vec![res.as_stmt(loc)], result_type),
712+
)
713+
}
714+
722715
fn codegen_exact_div(&mut self, mut fargs: Vec<Expr>, p: &Place<'tcx>, loc: Location) -> Stmt {
723716
// Check for undefined behavior conditions defined in
724717
// https://doc.rust-lang.org/std/intrinsics/fn.exact_div.html

kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use crate::kani_middle::coercion::{
1010
extract_unsize_casting, CoerceUnsizedInfo, CoerceUnsizedIterator, CoercionBase,
1111
};
1212
use crate::unwrap_or_return_codegen_unimplemented;
13-
use cbmc::goto_program::{Expr, Location, Stmt, Symbol, Type};
13+
use cbmc::goto_program::{
14+
arithmetic_overflow_result_type, BinaryOperator, Expr, Location, Stmt, Symbol, Type,
15+
ARITH_OVERFLOW_OVERFLOWED_FIELD, ARITH_OVERFLOW_RESULT_FIELD,
16+
};
1417
use cbmc::MachineModel;
1518
use cbmc::{btree_string_map, InternString, InternedString};
1619
use num::bigint::BigInt;
@@ -165,6 +168,37 @@ impl<'tcx> GotocCtx<'tcx> {
165168
}
166169
}
167170

171+
/// Generate code for a binary operation with an overflow and returns a tuple (res, overflow).
172+
pub fn codegen_binop_with_overflow(
173+
&mut self,
174+
bin_op: BinaryOperator,
175+
left: Expr,
176+
right: Expr,
177+
expected_typ: Type,
178+
loc: Location,
179+
) -> Expr {
180+
// Create CBMC result type and add to the symbol table.
181+
let res_type = arithmetic_overflow_result_type(left.typ().clone());
182+
let tag = res_type.tag().unwrap();
183+
let struct_tag =
184+
self.ensure_struct(tag, tag, |_, _| res_type.components().unwrap().clone());
185+
let res = left.overflow_op(bin_op, right);
186+
// store the result in a temporary variable
187+
let (var, decl) = self.decl_temp_variable(struct_tag, Some(res), loc);
188+
// cast into result type
189+
let cast = Expr::struct_expr_from_values(
190+
expected_typ.clone(),
191+
vec![
192+
var.clone().member(ARITH_OVERFLOW_RESULT_FIELD, &self.symbol_table),
193+
var.member(ARITH_OVERFLOW_OVERFLOWED_FIELD, &self.symbol_table)
194+
.cast_to(Type::c_bool()),
195+
],
196+
&self.symbol_table,
197+
);
198+
Expr::statement_expression(vec![decl, cast.as_stmt(loc)], expected_typ)
199+
}
200+
201+
/// Generate code for a binary arithmetic operation with UB / overflow checks in place.
168202
fn codegen_rvalue_checked_binary_op(
169203
&mut self,
170204
op: &BinOp,
@@ -199,27 +233,33 @@ impl<'tcx> GotocCtx<'tcx> {
199233

200234
match op {
201235
BinOp::Add => {
202-
let res = ce1.add_overflow(ce2);
203-
Expr::struct_expr_from_values(
204-
self.codegen_ty(res_ty),
205-
vec![res.result, res.overflowed.cast_to(Type::c_bool())],
206-
&self.symbol_table,
236+
let res_type = self.codegen_ty(res_ty);
237+
self.codegen_binop_with_overflow(
238+
BinaryOperator::OverflowResultPlus,
239+
ce1,
240+
ce2,
241+
res_type,
242+
Location::None,
207243
)
208244
}
209245
BinOp::Sub => {
210-
let res = ce1.sub_overflow(ce2);
211-
Expr::struct_expr_from_values(
212-
self.codegen_ty(res_ty),
213-
vec![res.result, res.overflowed.cast_to(Type::c_bool())],
214-
&self.symbol_table,
246+
let res_type = self.codegen_ty(res_ty);
247+
self.codegen_binop_with_overflow(
248+
BinaryOperator::OverflowResultMinus,
249+
ce1,
250+
ce2,
251+
res_type,
252+
Location::None,
215253
)
216254
}
217255
BinOp::Mul => {
218-
let res = ce1.mul_overflow(ce2);
219-
Expr::struct_expr_from_values(
220-
self.codegen_ty(res_ty),
221-
vec![res.result, res.overflowed.cast_to(Type::c_bool())],
222-
&self.symbol_table,
256+
let res_type = self.codegen_ty(res_ty);
257+
self.codegen_binop_with_overflow(
258+
BinaryOperator::OverflowResultMult,
259+
ce1,
260+
ce2,
261+
res_type,
262+
Location::None,
223263
)
224264
}
225265
BinOp::Shl => {

0 commit comments

Comments
 (0)