Skip to content

Commit 5ea97b3

Browse files
committed
Add overflow_result_exprt: compute result and overflow in one expression
Doing both the arithmetic operation and determining whether or not there was an overflow previously required either multiple expressions or going via `__builtin_X_overflow`, which should be specific to the C front-end. The new expression returns a struct with two components, the first of which has the type of the arithmetic operation, and the second is Boolean. Fixes: #6841
1 parent 5ce3bc3 commit 5ea97b3

10 files changed

+812
-177
lines changed

src/goto-programs/goto_convert_side_effect.cpp

+52-78
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ Author: Daniel Kroening, [email protected]
1212
#include "goto_convert_class.h"
1313

1414
#include <util/arith_tools.h>
15+
#include <util/bitvector_expr.h>
16+
#include <util/c_types.h>
1517
#include <util/expr_util.h>
1618
#include <util/fresh_symbol.h>
1719
#include <util/mathematical_types.h>
1820
#include <util/std_expr.h>
1921
#include <util/symbol.h>
2022

21-
#include <util/c_types.h>
22-
2323
#include <ansi-c/c_expr.h>
2424

2525
bool goto_convertt::has_function_call(const exprt &expr)
@@ -589,91 +589,65 @@ void goto_convertt::remove_overflow(
589589
const exprt &rhs = expr.rhs();
590590
const exprt &result = expr.result();
591591

592-
// actual logic implementing the operators: perform operations on signed
593-
// bitvector types of sufficiently large size to hold the result
594-
auto const make_operation = [&statement](exprt lhs, exprt rhs) -> exprt {
595-
std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width();
596-
if(lhs.type().id() == ID_unsignedbv)
597-
++lhs_ssize;
598-
std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width();
599-
if(rhs.type().id() == ID_unsignedbv)
600-
++rhs_ssize;
601-
602-
if(statement == ID_overflow_plus)
603-
{
604-
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
605-
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
606-
return plus_exprt{typecast_exprt{lhs, ssize_type},
607-
typecast_exprt{rhs, ssize_type}};
608-
}
609-
else if(statement == ID_overflow_minus)
592+
if(result.type().id() != ID_pointer)
593+
{
594+
// result of the arithmetic operation is _not_ used, just evaluate side
595+
// effects
596+
exprt tmp = result;
597+
clean_expr(tmp, dest, mode, false);
598+
599+
// the is-there-an-overflow result may be used
600+
if(result_is_used)
610601
{
611-
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
612-
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
613-
return minus_exprt{typecast_exprt{lhs, ssize_type},
614-
typecast_exprt{rhs, ssize_type}};
602+
binary_overflow_exprt overflow_check{
603+
typecast_exprt::conditional_cast(lhs, result.type()),
604+
statement,
605+
typecast_exprt::conditional_cast(rhs, result.type())};
606+
overflow_check.add_source_location() = expr.source_location();
607+
expr.swap(overflow_check);
615608
}
616609
else
617-
{
618-
INVARIANT(
619-
statement == ID_overflow_mult,
620-
"the three overflow operations are add, sub and mul");
621-
std::size_t ssize = lhs_ssize + rhs_ssize;
622-
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
623-
return mult_exprt{typecast_exprt{lhs, ssize_type},
624-
typecast_exprt{rhs, ssize_type}};
625-
}
626-
};
627-
628-
// Generating the following sequence of statements:
629-
// large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
630-
// *result = (result_type)tmp; // only if result is a pointer
631-
// (large_signedbv)(result_type)tmp != tmp;
632-
// This performs the operation (+, -, *) on a signed bitvector type of
633-
// sufficiently large width to store the precise result, cast to result
634-
// type, check if the cast result is not equivalent to the full-length
635-
// result.
636-
auto operation = make_operation(lhs, rhs);
637-
// Disable overflow checks as the operation cannot overflow on the larger
638-
// type
639-
operation.add_source_location() = expr.source_location();
640-
operation.add_source_location().add_pragma("disable:signed-overflow-check");
641-
642-
make_temp_symbol(operation, "large_bv", dest, mode);
643-
644-
optionalt<typet> result_type;
645-
if(result.type().id() == ID_pointer)
646-
{
647-
result_type = to_pointer_type(result.type()).base_type();
648-
code_assignt result_assignment{dereference_exprt{result},
649-
typecast_exprt{operation, *result_type},
650-
expr.source_location()};
651-
convert_assign(result_assignment, dest, mode);
610+
expr.make_nil();
652611
}
653612
else
654613
{
655-
result_type = result.type();
656-
// evaluate side effects
657-
exprt tmp = result;
658-
clean_expr(tmp, dest, mode, false); // result _not_ used
659-
}
660-
661-
if(result_is_used)
662-
{
663-
typecast_exprt inner_tc{operation, *result_type};
664-
inner_tc.add_source_location() = expr.source_location();
665-
inner_tc.add_source_location().add_pragma("disable:conversion-check");
666-
typecast_exprt outer_tc{inner_tc, operation.type()};
667-
outer_tc.add_source_location() = expr.source_location();
668-
outer_tc.add_source_location().add_pragma("disable:conversion-check");
614+
const typet &arith_type = to_pointer_type(result.type()).base_type();
615+
irep_idt arithmetic_operation =
616+
statement == ID_overflow_plus
617+
? ID_plus
618+
: statement == ID_overflow_minus
619+
? ID_minus
620+
: statement == ID_overflow_mult ? ID_mult : ID_nil;
621+
CHECK_RETURN(arithmetic_operation != ID_nil);
622+
exprt overflow_with_result = overflow_result_exprt{
623+
typecast_exprt::conditional_cast(lhs, arith_type),
624+
arithmetic_operation,
625+
typecast_exprt::conditional_cast(rhs, arith_type)};
626+
overflow_with_result.add_source_location() = expr.source_location();
627+
628+
if(result_is_used)
629+
make_temp_symbol(overflow_with_result, "overflow_result", dest, mode);
630+
631+
const struct_typet::componentst &result_comps =
632+
to_struct_type(overflow_with_result.type()).components();
633+
CHECK_RETURN(result_comps.size() == 2);
634+
code_assignt result_assignment{
635+
dereference_exprt{result},
636+
typecast_exprt::conditional_cast(
637+
member_exprt{overflow_with_result, result_comps[0]}, arith_type),
638+
expr.source_location()};
639+
convert_assign(result_assignment, dest, mode);
669640

670-
notequal_exprt overflow_check{outer_tc, operation};
671-
overflow_check.add_source_location() = expr.source_location();
641+
if(result_is_used)
642+
{
643+
member_exprt overflow_check{overflow_with_result, result_comps[1]};
644+
overflow_check.add_source_location() = expr.source_location();
672645

673-
expr.swap(overflow_check);
646+
expr.swap(overflow_check);
647+
}
648+
else
649+
expr.make_nil();
674650
}
675-
else
676-
expr.make_nil();
677651
}
678652

679653
void goto_convertt::remove_side_effect(

src/solvers/flattening/boolbv.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,12 @@ bvt boolbvt::convert_bitvector(const exprt &expr)
228228
return convert_bitreverse(to_bitreverse_expr(expr));
229229
else if(expr.id() == ID_saturating_minus || expr.id() == ID_saturating_plus)
230230
return convert_saturating_add_sub(to_binary_expr(expr));
231+
else if(
232+
const auto overflow_with_result =
233+
expr_try_dynamic_cast<overflow_result_exprt>(expr))
234+
{
235+
return convert_overflow_result(*overflow_with_result);
236+
}
231237

232238
return conversion_failed(expr);
233239
}

src/solvers/flattening/boolbv.h

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class extractbits_exprt;
3838
class floatbv_typecast_exprt;
3939
class ieee_float_op_exprt;
4040
class member_exprt;
41+
class overflow_result_exprt;
4142
class replication_exprt;
4243
class unary_overflow_exprt;
4344
class union_typet;
@@ -197,6 +198,7 @@ class boolbvt:public arrayst
197198
const function_application_exprt &expr);
198199
virtual bvt convert_bitreverse(const bitreverse_exprt &expr);
199200
virtual bvt convert_saturating_add_sub(const binary_exprt &expr);
201+
virtual bvt convert_overflow_result(const overflow_result_exprt &expr);
200202

201203
void convert_with(
202204
const typet &type,

0 commit comments

Comments
 (0)