Skip to content

Commit 03494ea

Browse files
committed
Implement GCC's __builtin_{add,sub,mul}_overflow_p
Refactor and expand the implementation of __builtin_{add,sub,mul}_overflow (i.e., without the _p suffix) to also support the variant that does not store the computed result.
1 parent 45b1a1e commit 03494ea

File tree

5 files changed

+69
-27
lines changed

5 files changed

+69
-27
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ 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+
113124
void check_non_const()
114125
{
115126
int a, b, c, d, r;
@@ -128,5 +139,6 @@ int main(void)
128139
check_unsigned_long();
129140
check_unsigned_long_long();
130141
check_generic();
142+
check_generic_p();
131143
check_non_const();
132144
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ 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
6065
\[check_non_const\.assertion\.1\] line \d+ assertion !__builtin_add_overflow\(a, b, &r\): FAILURE
6166
\[check_non_const\.assertion\.2\] line \d+ assertion __builtin_add_overflow\(c, d, &c\): FAILURE
6267
VERIFICATION FAILED

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: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ 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

604604
// actual logic implementing the operators: perform operations on signed
605605
// bitvector types of sufficiently large size to hold the result
@@ -639,8 +639,8 @@ void goto_convertt::remove_overflow(
639639

640640
// Generating the following sequence of statements:
641641
// large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
642-
// *result = (result_type)tmp;
643-
// (large_signedbv)*result != tmp;
642+
// *result = (result_type)tmp; // only if result is a pointer
643+
// (large_signedbv)(result_type)tmp != tmp;
644644
// This performs the operation (+, -, *) on a signed bitvector type of
645645
// sufficiently large width to store the precise result, cast to result
646646
// type, check if the cast result is not equivalent to the full-length
@@ -653,17 +653,33 @@ void goto_convertt::remove_overflow(
653653

654654
make_temp_symbol(operation, "large_bv", dest, mode);
655655

656-
const auto &result_type = to_pointer_type(result_ptr.type()).subtype();
657-
code_assignt result_assignment{dereference_exprt{result_ptr},
658-
typecast_exprt{operation, result_type},
659-
expr.source_location()};
660-
convert_assign(result_assignment, dest, mode);
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+
}
661672

662673
if(result_is_used)
663674
{
664-
notequal_exprt overflow_check{
665-
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
666-
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};
667683
overflow_check.add_source_location() = expr.source_location();
668684

669685
expr.swap(overflow_check);

0 commit comments

Comments
 (0)