Skip to content

Commit a22995d

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 1fac799 commit a22995d

File tree

5 files changed

+68
-25
lines changed

5 files changed

+68
-25
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_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

@@ -2997,7 +2998,10 @@ exprt c_typecheck_baset::do_special_functions(
29972998
else if(
29982999
identifier == "__builtin_add_overflow" ||
29993000
identifier == "__builtin_sub_overflow" ||
3000-
identifier == "__builtin_mul_overflow")
3001+
identifier == "__builtin_mul_overflow" ||
3002+
identifier == "__builtin_add_overflow_p" ||
3003+
identifier == "__builtin_sub_overflow_p" ||
3004+
identifier == "__builtin_mul_overflow_p")
30013005
{
30023006
// check function signature
30033007
if(expr.arguments().size() != 3)
@@ -3013,47 +3017,52 @@ exprt c_typecheck_baset::do_special_functions(
30133017

30143018
auto lhs = expr.arguments()[0];
30153019
auto rhs = expr.arguments()[1];
3016-
auto result_ptr = expr.arguments()[2];
3020+
auto result = expr.arguments()[2];
3021+
3022+
const bool is__p_variant = has_suffix(id2string(identifier), "_p");
30173023

30183024
{
30193025
auto const raise_wrong_argument_error =
3020-
[this,
3021-
identifier](const exprt &wrong_argument, std::size_t argument_number) {
3026+
[this, identifier](
3027+
const exprt &wrong_argument, std::size_t argument_number, bool _p) {
30223028
std::ostringstream error_message;
30233029
error_message << wrong_argument.source_location().as_string() << ": "
30243030
<< identifier << " has signature " << identifier
3025-
<< "(integral, integral, integral*), "
3031+
<< "(integral, integral, integral" << (_p ? "" : "*")
3032+
<< "), "
30263033
<< "but argument " << argument_number << " ("
30273034
<< expr2c(wrong_argument, *this) << ") has type `"
30283035
<< type2c(wrong_argument.type(), *this) << '`';
30293036
throw invalid_source_file_exceptiont{error_message.str()};
30303037
};
3031-
for(auto const arg_index : {0, 1})
3038+
for(int arg_index = 0; arg_index <= (!is__p_variant ? 1 : 2); ++arg_index)
30323039
{
30333040
auto const &argument = expr.arguments()[arg_index];
30343041

30353042
if(!is_signed_or_unsigned_bitvector(argument.type()))
30363043
{
3037-
raise_wrong_argument_error(argument, arg_index + 1);
3044+
raise_wrong_argument_error(argument, arg_index + 1, is__p_variant);
30383045
}
30393046
}
30403047
if(
3041-
result_ptr.type().id() != ID_pointer ||
3042-
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype()))
3048+
!is__p_variant &&
3049+
(result.type().id() != ID_pointer ||
3050+
!is_signed_or_unsigned_bitvector(result.type().subtype())))
30433051
{
3044-
raise_wrong_argument_error(result_ptr, 3);
3052+
raise_wrong_argument_error(result, 3, is__p_variant);
30453053
}
30463054
}
30473055

30483056
irep_idt kind =
3049-
(identifier == "__builtin_add_overflow")
3057+
has_prefix(id2string(identifier), "__builtin_add_overflow")
30503058
? ID_plus
3051-
: (identifier == "__builtin_sub_overflow") ? ID_minus : ID_mult;
3059+
: has_prefix(id2string(identifier), "__builtin_sub_overflow") ? ID_minus
3060+
: ID_mult;
30523061

30533062
return side_effect_expr_overflowt{kind,
30543063
std::move(lhs),
30553064
std::move(rhs),
3056-
std::move(result_ptr),
3065+
std::move(result),
30573066
expr.source_location()};
30583067
}
30593068
else

src/goto-programs/goto_convert_side_effect.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ void goto_convertt::remove_overflow(
597597
const irep_idt &statement = expr.get_statement();
598598
const exprt &lhs = expr.a();
599599
const exprt &rhs = expr.b();
600-
const exprt &result_ptr = expr.result();
600+
const exprt &result = expr.result();
601601

602602
// actual logic implementing the operators: perform operations on signed
603603
// bitvector types of sufficiently large size to hold the result
@@ -637,8 +637,8 @@ void goto_convertt::remove_overflow(
637637

638638
// Generating the following sequence of statements:
639639
// large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
640-
// *result = (result_type)tmp;
641-
// (large_signedbv)*result != tmp;
640+
// *result = (result_type)tmp; // only if result is a pointer
641+
// (large_signedbv)(result_type)tmp != tmp;
642642
// This performs the operation (+, -, *) on a signed bitvector type of
643643
// sufficiently large width to store the precise result, cast to result
644644
// type, check if the cast result is not equivalent to the full-length
@@ -651,17 +651,33 @@ void goto_convertt::remove_overflow(
651651

652652
make_temp_symbol(operation, "large_bv", dest, mode);
653653

654-
const auto &result_type = to_pointer_type(result_ptr.type()).subtype();
655-
code_assignt result_assignment{dereference_exprt{result_ptr},
656-
typecast_exprt{operation, result_type},
657-
expr.source_location()};
658-
convert_assign(result_assignment, dest, mode);
654+
optionalt<typet> result_type;
655+
if(result.type().id() == ID_pointer)
656+
{
657+
result_type = to_pointer_type(result.type()).subtype();
658+
code_assignt result_assignment{dereference_exprt{result},
659+
typecast_exprt{operation, *result_type},
660+
expr.source_location()};
661+
convert_assign(result_assignment, dest, mode);
662+
}
663+
else
664+
{
665+
result_type = result.type();
666+
// evaluate side effects
667+
exprt tmp = result;
668+
clean_expr(tmp, dest, mode, false); // result _not_ used
669+
}
659670

660671
if(result_is_used)
661672
{
662-
notequal_exprt overflow_check{
663-
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
664-
operation};
673+
typecast_exprt inner_tc{operation, *result_type};
674+
inner_tc.add_source_location() = expr.source_location();
675+
inner_tc.add_source_location().add_pragma("disable:conversion-check");
676+
typecast_exprt outer_tc{inner_tc, operation.type()};
677+
outer_tc.add_source_location() = expr.source_location();
678+
outer_tc.add_source_location().add_pragma("disable:conversion-check");
679+
680+
notequal_exprt overflow_check{outer_tc, operation};
665681
overflow_check.add_source_location() = expr.source_location();
666682

667683
expr.swap(overflow_check);

src/util/std_code.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2563,7 +2563,8 @@ inline code_function_bodyt &to_code_function_body(codet &code)
25632563

25642564
/// \brief A Boolean expression returning true, iff the result of performing
25652565
/// operation \c kind on operands \c a and \c b in infinite-precision arithmetic
2566-
/// cannot be represented in the type of the object that \c result points to.
2566+
/// cannot be represented in the type of the object that \c result points to (or
2567+
/// the type of \c result, if it is not a pointer).
25672568
/// If \c result is a pointer, the result of the operation is stored in the
25682569
/// object pointed to by \c result.
25692570
class side_effect_expr_overflowt : public side_effect_exprt

0 commit comments

Comments
 (0)