Skip to content

Commit f03779a

Browse files
authored
Merge pull request #5544 from tautschnig/fix-__builtin_X_overflow
Use bitvector types to implement __builtin_{add,sub,mul}_overflow
2 parents 390f5c2 + e789c67 commit f03779a

File tree

5 files changed

+118
-39
lines changed

5 files changed

+118
-39
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ void check_generic(void)
110110
assert(0 && "reachability");
111111
}
112112

113+
void check_generic_p(void)
114+
{
115+
unsigned char small_result = 0;
116+
signed long long big_result = 0;
117+
assert(!__builtin_add_overflow_p(17, 25, small_result));
118+
assert(small_result == 0);
119+
assert(!__builtin_add_overflow_p(17, 25, big_result));
120+
assert(big_result == 0);
121+
assert(0 && "reachability");
122+
}
123+
124+
void check_non_const()
125+
{
126+
int a, b, c, d, r;
127+
// this may overflow (negated assertion fails)
128+
assert(!__builtin_add_overflow(a, b, &r));
129+
// but need not overflow (assertion fails)
130+
assert(__builtin_add_overflow(c, d, &c));
131+
}
132+
113133
int main(void)
114134
{
115135
check_int();
@@ -119,4 +139,6 @@ int main(void)
119139
check_unsigned_long();
120140
check_unsigned_long_long();
121141
check_generic();
142+
check_generic_p();
143+
check_non_const();
122144
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ main.c
5757
\[check_generic.assertion.9\] line \d+ assertion big_result == 2ll \* .*: SUCCESS
5858
\[check_generic.assertion.10\] line \d+ assertion __builtin_add_overflow\(.* / 2 \+ 1, .*/ 2 \+ 1, &big_result\): SUCCESS
5959
\[check_generic.assertion.11\] line \d+ assertion 0 && "reachability": FAILURE
60+
\[check_generic_p.assertion.1\] line \d+ assertion !__builtin_add_overflow_p\(17, 25, small_result\): SUCCESS
61+
\[check_generic_p.assertion.2\] line \d+ assertion small_result == 0: SUCCESS
62+
\[check_generic_p.assertion.3\] line \d+ assertion !__builtin_add_overflow_p\(17, 25, big_result\): SUCCESS
63+
\[check_generic_p.assertion.4\] line \d+ assertion big_result == 0: SUCCESS
64+
\[check_generic_p.assertion.5\] line \d+ assertion 0 && "reachability": FAILURE
65+
\[check_non_const\.assertion\.1\] line \d+ assertion !__builtin_add_overflow\(a, b, &r\): FAILURE
66+
\[check_non_const\.assertion\.2\] line \d+ assertion __builtin_add_overflow\(c, d, &c\): FAILURE
6067
VERIFICATION FAILED
6168
^EXIT=10$
6269
^SIGNAL=0$

src/ansi-c/c_expr.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ inline shuffle_vector_exprt &to_shuffle_vector_expr(exprt &expr)
108108
}
109109

110110
/// \brief A Boolean expression returning true, iff the result of performing
111-
/// operation \c kind on operands \c lhs and \c rhs in infinite-precision
112-
/// arithmetic cannot be represented in the type of the object that \c result
113-
/// points to.
111+
/// operation \c kind on operands \c a and \c b in infinite-precision arithmetic
112+
/// cannot be represented in the type of the object that \c result points to (or
113+
/// the type of \c result, if it is not a pointer).
114114
/// If \c result is a pointer, the result of the operation is stored in the
115115
/// object pointed to by \c result.
116116
class side_effect_expr_overflowt : public side_effect_exprt

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Author: Daniel Kroening, [email protected]
2929
#include <util/range.h>
3030
#include <util/simplify_expr.h>
3131
#include <util/string_constant.h>
32+
#include <util/suffix.h>
3233

3334
#include <goto-programs/adjust_float_expressions.h>
3435

@@ -3012,7 +3013,10 @@ exprt c_typecheck_baset::do_special_functions(
30123013
else if(
30133014
identifier == "__builtin_add_overflow" ||
30143015
identifier == "__builtin_sub_overflow" ||
3015-
identifier == "__builtin_mul_overflow")
3016+
identifier == "__builtin_mul_overflow" ||
3017+
identifier == "__builtin_add_overflow_p" ||
3018+
identifier == "__builtin_sub_overflow_p" ||
3019+
identifier == "__builtin_mul_overflow_p")
30163020
{
30173021
// check function signature
30183022
if(expr.arguments().size() != 3)
@@ -3028,47 +3032,52 @@ exprt c_typecheck_baset::do_special_functions(
30283032

30293033
auto lhs = expr.arguments()[0];
30303034
auto rhs = expr.arguments()[1];
3031-
auto result_ptr = expr.arguments()[2];
3035+
auto result = expr.arguments()[2];
3036+
3037+
const bool is__p_variant = has_suffix(id2string(identifier), "_p");
30323038

30333039
{
30343040
auto const raise_wrong_argument_error =
3035-
[this,
3036-
identifier](const exprt &wrong_argument, std::size_t argument_number) {
3041+
[this, identifier](
3042+
const exprt &wrong_argument, std::size_t argument_number, bool _p) {
30373043
std::ostringstream error_message;
30383044
error_message << wrong_argument.source_location().as_string() << ": "
30393045
<< identifier << " has signature " << identifier
3040-
<< "(integral, integral, integral*), "
3046+
<< "(integral, integral, integral" << (_p ? "" : "*")
3047+
<< "), "
30413048
<< "but argument " << argument_number << " ("
30423049
<< expr2c(wrong_argument, *this) << ") has type `"
30433050
<< type2c(wrong_argument.type(), *this) << '`';
30443051
throw invalid_source_file_exceptiont{error_message.str()};
30453052
};
3046-
for(auto const arg_index : {0, 1})
3053+
for(int arg_index = 0; arg_index <= (!is__p_variant ? 1 : 2); ++arg_index)
30473054
{
30483055
auto const &argument = expr.arguments()[arg_index];
30493056

30503057
if(!is_signed_or_unsigned_bitvector(argument.type()))
30513058
{
3052-
raise_wrong_argument_error(argument, arg_index + 1);
3059+
raise_wrong_argument_error(argument, arg_index + 1, is__p_variant);
30533060
}
30543061
}
30553062
if(
3056-
result_ptr.type().id() != ID_pointer ||
3057-
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype()))
3063+
!is__p_variant &&
3064+
(result.type().id() != ID_pointer ||
3065+
!is_signed_or_unsigned_bitvector(result.type().subtype())))
30583066
{
3059-
raise_wrong_argument_error(result_ptr, 3);
3067+
raise_wrong_argument_error(result, 3, is__p_variant);
30603068
}
30613069
}
30623070

30633071
irep_idt kind =
3064-
(identifier == "__builtin_add_overflow")
3072+
has_prefix(id2string(identifier), "__builtin_add_overflow")
30653073
? ID_plus
3066-
: (identifier == "__builtin_sub_overflow") ? ID_minus : ID_mult;
3074+
: has_prefix(id2string(identifier), "__builtin_sub_overflow") ? ID_minus
3075+
: ID_mult;
30673076

30683077
return side_effect_expr_overflowt{kind,
30693078
std::move(lhs),
30703079
std::move(rhs),
3071-
std::move(result_ptr),
3080+
std::move(result),
30723081
expr.source_location()};
30733082
}
30743083
else

src/goto-programs/goto_convert_side_effect.cpp

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -599,48 +599,89 @@ void goto_convertt::remove_overflow(
599599
const irep_idt &statement = expr.get_statement();
600600
const exprt &lhs = expr.lhs();
601601
const exprt &rhs = expr.rhs();
602-
const exprt &result_ptr = expr.result();
602+
const exprt &result = expr.result();
603603

604-
// actual logic implementing the operators
604+
// actual logic implementing the operators: perform operations on signed
605+
// bitvector types of sufficiently large size to hold the result
605606
auto const make_operation = [&statement](exprt lhs, exprt rhs) -> exprt {
607+
std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width();
608+
if(lhs.type().id() == ID_unsignedbv)
609+
++lhs_ssize;
610+
std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width();
611+
if(rhs.type().id() == ID_unsignedbv)
612+
++rhs_ssize;
613+
606614
if(statement == ID_overflow_plus)
607615
{
608-
return plus_exprt{lhs, rhs};
616+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
617+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
618+
return plus_exprt{typecast_exprt{lhs, ssize_type},
619+
typecast_exprt{rhs, ssize_type}};
609620
}
610621
else if(statement == ID_overflow_minus)
611622
{
612-
return minus_exprt{lhs, rhs};
623+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
624+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
625+
return minus_exprt{typecast_exprt{lhs, ssize_type},
626+
typecast_exprt{rhs, ssize_type}};
613627
}
614628
else
615629
{
616630
INVARIANT(
617631
statement == ID_overflow_mult,
618632
"the three overflow operations are add, sub and mul");
619-
return mult_exprt{lhs, rhs};
633+
std::size_t ssize = lhs_ssize + rhs_ssize;
634+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
635+
return mult_exprt{typecast_exprt{lhs, ssize_type},
636+
typecast_exprt{rhs, ssize_type}};
620637
}
621638
};
622639

623-
// we’re basically generating this expression
624-
// (*result = (result_type)((integer)lhs OP (integer)rhs)),
625-
// ((integer)result == (integer)lhs OP (integer)rhs)
626-
// i.e. perform the operation (+, -, *) on arbitrary length integer,
627-
// cast to result type, check if the casted result is still equivalent
628-
// to the arbitrary length result.
629-
auto operation = make_operation(
630-
typecast_exprt{lhs, integer_typet{}}, typecast_exprt{rhs, integer_typet{}});
631-
632-
typecast_exprt operation_result{operation, result_ptr.type().subtype()};
633-
634-
code_assignt assign{dereference_exprt{result_ptr},
635-
std::move(operation_result),
636-
expr.source_location()};
637-
convert_assign(assign, dest, mode);
640+
// Generating the following sequence of statements:
641+
// large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
642+
// *result = (result_type)tmp; // only if result is a pointer
643+
// (large_signedbv)(result_type)tmp != tmp;
644+
// This performs the operation (+, -, *) on a signed bitvector type of
645+
// sufficiently large width to store the precise result, cast to result
646+
// type, check if the cast result is not equivalent to the full-length
647+
// result.
648+
auto operation = make_operation(lhs, rhs);
649+
// Disable overflow checks as the operation cannot overflow on the larger
650+
// type
651+
operation.add_source_location() = expr.source_location();
652+
operation.add_source_location().add_pragma("disable:signed-overflow-check");
653+
654+
make_temp_symbol(operation, "large_bv", dest, mode);
655+
656+
optionalt<typet> result_type;
657+
if(result.type().id() == ID_pointer)
658+
{
659+
result_type = to_pointer_type(result.type()).subtype();
660+
code_assignt result_assignment{dereference_exprt{result},
661+
typecast_exprt{operation, *result_type},
662+
expr.source_location()};
663+
convert_assign(result_assignment, dest, mode);
664+
}
665+
else
666+
{
667+
result_type = result.type();
668+
// evaluate side effects
669+
exprt tmp = result;
670+
clean_expr(tmp, dest, mode, false); // result _not_ used
671+
}
638672

639673
if(result_is_used)
640674
{
641-
notequal_exprt overflow_check{
642-
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
643-
operation};
675+
typecast_exprt inner_tc{operation, *result_type};
676+
inner_tc.add_source_location() = expr.source_location();
677+
inner_tc.add_source_location().add_pragma("disable:conversion-check");
678+
typecast_exprt outer_tc{inner_tc, operation.type()};
679+
outer_tc.add_source_location() = expr.source_location();
680+
outer_tc.add_source_location().add_pragma("disable:conversion-check");
681+
682+
notequal_exprt overflow_check{outer_tc, operation};
683+
overflow_check.add_source_location() = expr.source_location();
684+
644685
expr.swap(overflow_check);
645686
}
646687
else

0 commit comments

Comments
 (0)