Skip to content

Commit 3b4d6e2

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 1934f07 commit 3b4d6e2

File tree

3 files changed

+70
-22
lines changed

3 files changed

+70
-22
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: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Author: Daniel Kroening, [email protected]
3030
#include <util/range.h>
3131
#include <util/simplify_expr.h>
3232
#include <util/string_constant.h>
33+
#include <util/suffix.h>
3334

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

@@ -2965,7 +2966,10 @@ exprt c_typecheck_baset::do_special_functions(
29652966
else if(
29662967
identifier == "__builtin_add_overflow" ||
29672968
identifier == "__builtin_sub_overflow" ||
2968-
identifier == "__builtin_mul_overflow")
2969+
identifier == "__builtin_mul_overflow" ||
2970+
identifier == "__builtin_add_overflow_p" ||
2971+
identifier == "__builtin_sub_overflow_p" ||
2972+
identifier == "__builtin_mul_overflow_p")
29692973
{
29702974
// check function signature
29712975
if(expr.arguments().size() != 3)
@@ -2985,33 +2989,37 @@ exprt c_typecheck_baset::do_special_functions(
29852989
typecheck_expr(rhs);
29862990
typecheck_expr(result_ptr);
29872991

2992+
const bool is__p_variant = has_suffix(id2string(identifier), "_p");
2993+
29882994
{
29892995
auto const raise_wrong_argument_error =
2990-
[this,
2991-
identifier](const exprt &wrong_argument, std::size_t argument_number) {
2996+
[this, identifier](
2997+
const exprt &wrong_argument, std::size_t argument_number, bool _p) {
29922998
std::ostringstream error_message;
29932999
error_message << wrong_argument.source_location().as_string() << ": "
29943000
<< identifier << " has signature " << identifier
2995-
<< "(integral, integral, integral*), "
3001+
<< "(integral, integral, integral" << (_p ? "" : "*")
3002+
<< "), "
29963003
<< "but argument " << argument_number << " ("
29973004
<< expr2c(wrong_argument, *this) << ") has type `"
29983005
<< type2c(wrong_argument.type(), *this) << '`';
29993006
throw invalid_source_file_exceptiont{error_message.str()};
30003007
};
3001-
for(auto const arg_index : {0, 1})
3008+
for(int arg_index = 0; arg_index <= (!is__p_variant ? 1 : 2); ++arg_index)
30023009
{
30033010
auto const &argument = expr.arguments()[arg_index];
30043011

30053012
if(!is_signed_or_unsigned_bitvector(argument.type()))
30063013
{
3007-
raise_wrong_argument_error(argument, arg_index + 1);
3014+
raise_wrong_argument_error(argument, arg_index + 1, is__p_variant);
30083015
}
30093016
}
30103017
if(
3011-
result_ptr.type().id() != ID_pointer ||
3012-
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype()))
3018+
!is__p_variant &&
3019+
(result_ptr.type().id() != ID_pointer ||
3020+
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype())))
30133021
{
3014-
raise_wrong_argument_error(result_ptr, 3);
3022+
raise_wrong_argument_error(result_ptr, 3, is__p_variant);
30153023
}
30163024
}
30173025

@@ -3025,14 +3033,14 @@ exprt c_typecheck_baset::do_special_functions(
30253033
if(rhs.type().id() == ID_unsignedbv)
30263034
++rhs_ssize;
30273035

3028-
if(identifier == "__builtin_add_overflow")
3036+
if(has_prefix(id2string(identifier), "__builtin_add_overflow"))
30293037
{
30303038
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
30313039
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
30323040
return plus_exprt{typecast_exprt{lhs, ssize_type},
30333041
typecast_exprt{rhs, ssize_type}};
30343042
}
3035-
else if(identifier == "__builtin_sub_overflow")
3043+
else if(has_prefix(id2string(identifier), "__builtin_sub_overflow"))
30363044
{
30373045
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
30383046
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
@@ -3042,7 +3050,7 @@ exprt c_typecheck_baset::do_special_functions(
30423050
else
30433051
{
30443052
INVARIANT(
3045-
identifier == "__builtin_mul_overflow",
3053+
has_prefix(id2string(identifier), "__builtin_mul_overflow"),
30463054
"the three overflow operations are add, sub and mul");
30473055
std::size_t ssize = lhs_ssize + rhs_ssize;
30483056
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
@@ -3054,7 +3062,10 @@ exprt c_typecheck_baset::do_special_functions(
30543062
// Generating the following statement expression:
30553063
// ({ large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
30563064
// *result = (result_type)large_signedbv;
3057-
// (large_signedbv)*result != tmp; })
3065+
// (large_signedbv)(result_type)tmp != tmp; })
3066+
// The _p variant just uses result; in place of the assignment to *result.
3067+
code_blockt statements;
3068+
30583069
// This performs the operation (+, -, *) on a signed bitvector type of
30593070
// sufficiently large width to store the precise result, cast to result
30603071
// type, check if the casted result is not equivalent to the full-length
@@ -3075,23 +3086,43 @@ exprt c_typecheck_baset::do_special_functions(
30753086

30763087
code_declt decl{new_symbol.symbol_expr()};
30773088
decl.add_source_location() = expr.source_location();
3089+
statements.add(decl);
30783090

30793091
code_assignt tmp_assignment{decl.symbol(), operation};
30803092
tmp_assignment.add_source_location() = expr.source_location();
3093+
statements.add(tmp_assignment);
30813094

3082-
const auto &result_type = to_pointer_type(result_ptr.type()).subtype();
3083-
code_assignt result_assignment{dereference_exprt{result_ptr},
3084-
typecast_exprt{decl.symbol(), result_type}};
3085-
result_assignment.add_source_location() = expr.source_location();
3095+
typet result_type;
3096+
if(!is__p_variant)
3097+
{
3098+
result_type = to_pointer_type(result_ptr.type()).subtype();
3099+
code_assignt result_assignment{
3100+
dereference_exprt{result_ptr},
3101+
typecast_exprt{decl.symbol(), result_type}};
3102+
result_assignment.add_source_location() = expr.source_location();
3103+
statements.add(result_assignment);
3104+
}
3105+
else
3106+
{
3107+
result_type = result_ptr.type();
3108+
code_expressiont eval_side_effects{result_ptr};
3109+
eval_side_effects.add_source_location() = expr.source_location();
3110+
statements.add(eval_side_effects);
3111+
}
30863112

3087-
notequal_exprt overflow_check{
3088-
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
3089-
decl.symbol()};
3113+
typecast_exprt inner_tc{decl.symbol(), result_type};
3114+
inner_tc.add_source_location() = expr.source_location();
3115+
inner_tc.add_source_location().add_pragma("disable:conversion-check");
3116+
typecast_exprt outer_tc{inner_tc, operation.type()};
3117+
outer_tc.add_source_location() = expr.source_location();
3118+
outer_tc.add_source_location().add_pragma("disable:conversion-check");
3119+
notequal_exprt overflow_check{outer_tc, decl.symbol()};
30903120
overflow_check.add_source_location() = expr.source_location();
3121+
30913122
code_expressiont return_expr{overflow_check};
3123+
return_expr.add_source_location() = expr.source_location();
3124+
statements.add(return_expr);
30923125

3093-
code_blockt statements{
3094-
{decl, tmp_assignment, result_assignment, return_expr}};
30953126
return side_effect_expr_statement_expressiont{
30963127
statements, overflow_check.type(), expr.source_location()};
30973128
}

0 commit comments

Comments
 (0)