Skip to content

Commit 4d540eb

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 0a18c74 commit 4d540eb

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\(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\(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
@@ -28,6 +28,7 @@ Author: Daniel Kroening, [email protected]
2828
#include <util/prefix.h>
2929
#include <util/simplify_expr.h>
3030
#include <util/string_constant.h>
31+
#include <util/suffix.h>
3132

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

@@ -2813,7 +2814,10 @@ exprt c_typecheck_baset::do_special_functions(
28132814
else if(
28142815
identifier == "__builtin_add_overflow" ||
28152816
identifier == "__builtin_sub_overflow" ||
2816-
identifier == "__builtin_mul_overflow")
2817+
identifier == "__builtin_mul_overflow" ||
2818+
identifier == "__builtin_add_overflow_p" ||
2819+
identifier == "__builtin_sub_overflow_p" ||
2820+
identifier == "__builtin_mul_overflow_p")
28172821
{
28182822
// check function signature
28192823
if(expr.arguments().size() != 3)
@@ -2833,33 +2837,37 @@ exprt c_typecheck_baset::do_special_functions(
28332837
typecheck_expr(rhs);
28342838
typecheck_expr(result_ptr);
28352839

2840+
const bool is__p_variant = has_suffix(id2string(identifier), "_p");
2841+
28362842
{
28372843
auto const raise_wrong_argument_error =
2838-
[this,
2839-
identifier](const exprt &wrong_argument, std::size_t argument_number) {
2844+
[this, identifier](
2845+
const exprt &wrong_argument, std::size_t argument_number, bool _p) {
28402846
std::ostringstream error_message;
28412847
error_message << wrong_argument.source_location().as_string() << ": "
28422848
<< identifier << " has signature " << identifier
2843-
<< "(integral, integral, integral*), "
2849+
<< "(integral, integral, integral" << (_p ? "" : "*")
2850+
<< "), "
28442851
<< "but argument " << argument_number << " ("
28452852
<< expr2c(wrong_argument, *this) << ") has type `"
28462853
<< type2c(wrong_argument.type(), *this) << '`';
28472854
throw invalid_source_file_exceptiont{error_message.str()};
28482855
};
2849-
for(auto const arg_index : {0, 1})
2856+
for(int arg_index = 0; arg_index <= (!is__p_variant ? 1 : 2); ++arg_index)
28502857
{
28512858
auto const &argument = expr.arguments()[arg_index];
28522859

28532860
if(!is_signed_or_unsigned_bitvector(argument.type()))
28542861
{
2855-
raise_wrong_argument_error(argument, arg_index + 1);
2862+
raise_wrong_argument_error(argument, arg_index + 1, is__p_variant);
28562863
}
28572864
}
28582865
if(
2859-
result_ptr.type().id() != ID_pointer ||
2860-
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype()))
2866+
!is__p_variant &&
2867+
(result_ptr.type().id() != ID_pointer ||
2868+
!is_signed_or_unsigned_bitvector(result_ptr.type().subtype())))
28612869
{
2862-
raise_wrong_argument_error(result_ptr, 3);
2870+
raise_wrong_argument_error(result_ptr, 3, is__p_variant);
28632871
}
28642872
}
28652873

@@ -2873,14 +2881,14 @@ exprt c_typecheck_baset::do_special_functions(
28732881
if(rhs.type().id() == ID_unsignedbv)
28742882
++rhs_ssize;
28752883

2876-
if(identifier == "__builtin_add_overflow")
2884+
if(has_prefix(id2string(identifier), "__builtin_add_overflow"))
28772885
{
28782886
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
28792887
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
28802888
return plus_exprt{typecast_exprt{lhs, ssize_type},
28812889
typecast_exprt{rhs, ssize_type}};
28822890
}
2883-
else if(identifier == "__builtin_sub_overflow")
2891+
else if(has_prefix(id2string(identifier), "__builtin_sub_overflow"))
28842892
{
28852893
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
28862894
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
@@ -2890,7 +2898,7 @@ exprt c_typecheck_baset::do_special_functions(
28902898
else
28912899
{
28922900
INVARIANT(
2893-
identifier == "__builtin_mul_overflow",
2901+
has_prefix(id2string(identifier), "__builtin_mul_overflow"),
28942902
"the three overflow operations are add, sub and mul");
28952903
std::size_t ssize = lhs_ssize + rhs_ssize;
28962904
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
@@ -2902,7 +2910,10 @@ exprt c_typecheck_baset::do_special_functions(
29022910
// Generating the following statement expression:
29032911
// ({ large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
29042912
// *result = (result_type)large_signedbv;
2905-
// (large_signedbv)*result != tmp; })
2913+
// (large_signedbv)(result_type)tmp != tmp; })
2914+
// The _p variant just uses result; in place of the assignment to *result.
2915+
code_blockt statements;
2916+
29062917
// This performs the operation (+, -, *) on a signed bitvector type of
29072918
// sufficiently large width to store the precise result, cast to result
29082919
// type, check if the casted result is not equivalent to the full-length
@@ -2923,23 +2934,43 @@ exprt c_typecheck_baset::do_special_functions(
29232934

29242935
code_declt decl{new_symbol.symbol_expr()};
29252936
decl.add_source_location() = expr.source_location();
2937+
statements.add(decl);
29262938

29272939
code_assignt tmp_assignment{decl.symbol(), operation};
29282940
tmp_assignment.add_source_location() = expr.source_location();
2941+
statements.add(tmp_assignment);
29292942

2930-
const auto &result_type = to_pointer_type(result_ptr.type()).subtype();
2931-
code_assignt result_assignment{dereference_exprt{result_ptr},
2932-
typecast_exprt{decl.symbol(), result_type}};
2933-
result_assignment.add_source_location() = expr.source_location();
2943+
typet result_type;
2944+
if(!is__p_variant)
2945+
{
2946+
result_type = to_pointer_type(result_ptr.type()).subtype();
2947+
code_assignt result_assignment{
2948+
dereference_exprt{result_ptr},
2949+
typecast_exprt{decl.symbol(), result_type}};
2950+
result_assignment.add_source_location() = expr.source_location();
2951+
statements.add(result_assignment);
2952+
}
2953+
else
2954+
{
2955+
result_type = result_ptr.type();
2956+
code_expressiont eval_side_effects{result_ptr};
2957+
eval_side_effects.add_source_location() = expr.source_location();
2958+
statements.add(eval_side_effects);
2959+
}
29342960

2935-
notequal_exprt overflow_check{
2936-
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
2937-
decl.symbol()};
2961+
typecast_exprt inner_tc{decl.symbol(), result_type};
2962+
inner_tc.add_source_location() = expr.source_location();
2963+
inner_tc.add_source_location().add_pragma("disable:conversion-check");
2964+
typecast_exprt outer_tc{inner_tc, operation.type()};
2965+
outer_tc.add_source_location() = expr.source_location();
2966+
outer_tc.add_source_location().add_pragma("disable:conversion-check");
2967+
notequal_exprt overflow_check{outer_tc, decl.symbol()};
29382968
overflow_check.add_source_location() = expr.source_location();
2969+
29392970
code_expressiont return_expr{overflow_check};
2971+
return_expr.add_source_location() = expr.source_location();
2972+
statements.add(return_expr);
29402973

2941-
code_blockt statements{
2942-
{decl, tmp_assignment, result_assignment, return_expr}};
29432974
return side_effect_expr_statement_expressiont{
29442975
statements, overflow_check.type(), expr.source_location()};
29452976
}

0 commit comments

Comments
 (0)