Skip to content

Commit 4638bbe

Browse files
Add generic gcc __builtin_*_overflow operators
GCC has builtin operators `__builtin_add_overflow`, `__builtin_sub_overflow` and `__builtin_mul_overflow` that perform an arithmetic operation and check for overflow at the same time. This adds support for these operators.
1 parent cd2c65b commit 4638bbe

File tree

7 files changed

+170
-4
lines changed

7 files changed

+170
-4
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,31 @@ void check_unsigned_long_long(void)
8282
unsigned long long const one = 1ull;
8383
unsigned long long result;
8484

85-
assert(!__builtin_uaddl_overflow(one, one, &result));
85+
assert(!__builtin_uaddll_overflow(one, one, &result));
8686
assert(result == 2ull);
8787
assert(!__builtin_uaddll_overflow(ULLONG_MAX / 2, ULLONG_MAX / 2, &result));
8888
assert(result + 1ull == ULLONG_MAX);
8989
assert(
9090
__builtin_uaddll_overflow(ULLONG_MAX / 2 + 2, ULLONG_MAX / 2 + 2, &result));
91-
assert(__builtin_uaddl_overflow(one, ULLONG_MAX, &result));
91+
assert(__builtin_uaddll_overflow(one, ULLONG_MAX, &result));
92+
assert(0 && "reachability");
93+
}
94+
95+
void check_generic(void)
96+
{
97+
unsigned char small_result;
98+
signed long long big_result;
99+
assert(!__builtin_add_overflow(17, 25, &small_result));
100+
assert(small_result == 42);
101+
assert(!__builtin_add_overflow(17, 25, &big_result));
102+
assert(big_result == 42ll);
103+
assert(__builtin_add_overflow(216, 129, &small_result));
104+
assert(!__builtin_add_overflow(216, 129, &big_result));
105+
assert(big_result == 345);
106+
assert(!__builtin_add_overflow(INT_MAX, INT_MAX, &big_result));
107+
assert(big_result == 2ll * INT_MAX);
108+
assert(
109+
__builtin_add_overflow(LLONG_MAX / 2 + 1, LLONG_MAX / 2 + 1, &big_result));
92110
assert(0 && "reachability");
93111
}
94112

@@ -100,4 +118,5 @@ int main(void)
100118
check_unsigned();
101119
check_unsigned_long();
102120
check_unsigned_long_long();
121+
check_generic();
103122
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,24 @@ main.c
3939
\[check_unsigned_long.assertion.5\] line \d+ assertion __builtin_uaddl_overflow\(.* / 2 \+ 2, .* / 2 \+ 2, &result\): SUCCESS
4040
\[check_unsigned_long.assertion.6\] line \d+ assertion __builtin_uaddl_overflow\(one, .*, &result\): SUCCESS
4141
\[check_unsigned_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
42-
\[check_unsigned_long_long.assertion.1\] line \d+ assertion !__builtin_uaddl_overflow\(one, one, &result\): SUCCESS
42+
\[check_unsigned_long_long.assertion.1\] line \d+ assertion !__builtin_uaddll_overflow\(one, one, &result\): SUCCESS
4343
\[check_unsigned_long_long.assertion.2\] line \d+ assertion result == 2ull: SUCCESS
4444
\[check_unsigned_long_long.assertion.3\] line \d+ assertion !__builtin_uaddll_overflow\(.* / 2, .* / 2, &result\): SUCCESS
4545
\[check_unsigned_long_long.assertion.4\] line \d+ assertion result \+ 1ull == .*: SUCCESS
4646
\[check_unsigned_long_long.assertion.5\] line \d+ assertion __builtin_uaddll_overflow\(.* / 2 \+ 2, .* / 2 \+ 2, &result\): SUCCESS
47-
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_uaddl_overflow\(one, .*, &result\): SUCCESS
47+
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_uaddll_overflow\(one, .*, &result\): SUCCESS
4848
\[check_unsigned_long_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
49+
\[check_generic.assertion.1\] line \d+ assertion !__builtin_add_overflow\(17, 25, &small_result\): SUCCESS
50+
\[check_generic.assertion.2\] line \d+ assertion small_result == 42: SUCCESS
51+
\[check_generic.assertion.3\] line \d+ assertion !__builtin_add_overflow\(17, 25, &big_result\): SUCCESS
52+
\[check_generic.assertion.4\] line \d+ assertion big_result == 42ll: SUCCESS
53+
\[check_generic.assertion.5\] line \d+ assertion __builtin_add_overflow\(216, 129, &small_result\): SUCCESS
54+
\[check_generic.assertion.6\] line \d+ assertion !__builtin_add_overflow\(216, 129, &big_result\): SUCCESS
55+
\[check_generic.assertion.7\] line \d+ assertion big_result == 345: SUCCESS
56+
\[check_generic.assertion.8\] line \d+ assertion !__builtin_add_overflow\(.*, .*, &big_result\): SUCCESS
57+
\[check_generic.assertion.9\] line \d+ assertion big_result == 2ll \* .*: SUCCESS
58+
\[check_generic.assertion.10\] line \d+ assertion __builtin_add_overflow\(.* / 2 \+ 1, .*/ 2 \+ 1, &big_result\): SUCCESS
59+
\[check_generic.assertion.11\] line \d+ assertion 0 && "reachability": FAILURE
4960
VERIFICATION FAILED
5061
^EXIT=10$
5162
^SIGNAL=0$

regression/cbmc/gcc_builtin_mul_overflow/main.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,22 @@ void check_unsigned_long_long(void)
104104
assert(0 && "reachability");
105105
}
106106

107+
void check_generic(void)
108+
{
109+
unsigned char small_result;
110+
long long big_result;
111+
112+
assert(__builtin_mul_overflow(100, 100, &small_result));
113+
assert(!__builtin_mul_overflow(100, 100, &big_result));
114+
assert(big_result == 10000);
115+
assert(!__builtin_mul_overflow(10, 10, &small_result));
116+
assert(small_result == 100);
117+
assert(!__builtin_mul_overflow(INT_MAX, INT_MAX, &big_result));
118+
assert(big_result == 4611686014132420609ll);
119+
assert(__builtin_mul_overflow(INT_MAX * 2ll, INT_MAX * 2ll, &big_result));
120+
assert(0 && "reachability");
121+
}
122+
107123
int main(void)
108124
{
109125
check_int();
@@ -112,5 +128,6 @@ int main(void)
112128
check_unsigned();
113129
check_unsigned_long();
114130
check_unsigned_long_long();
131+
check_generic();
115132
return 0;
116133
}

regression/cbmc/gcc_builtin_mul_overflow/test.desc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,14 @@ main.c
3737
\[check_unsigned_long_long.assertion.4\] line \d+ assertion result == lt_isqrt_of_unsigned_long_long_max \* lt_isqrt_of_unsigned_long_long_max: SUCCESS
3838
\[check_unsigned_long_long.assertion.5\] line \d+ assertion __builtin_umulll_overflow\( lt_isqrt_of_unsigned_long_long_max << 1, lt_isqrt_of_unsigned_long_long_max << 1, &result\): SUCCESS
3939
\[check_unsigned_long_long.assertion.6\] line \d+ assertion 0 && "reachability": FAILURE
40+
\[check_generic.assertion.1\] line \d+ assertion __builtin_mul_overflow\(100, 100, &small_result\): SUCCESS
41+
\[check_generic.assertion.2\] line \d+ assertion !__builtin_mul_overflow\(100, 100, &big_result\): SUCCESS
42+
\[check_generic.assertion.3\] line \d+ assertion big_result == 10000: SUCCESS
43+
\[check_generic.assertion.4\] line \d+ assertion !__builtin_mul_overflow\(10, 10, &small_result\): SUCCESS
44+
\[check_generic.assertion.5\] line \d+ assertion small_result == 100: SUCCESS
45+
\[check_generic.assertion.6\] line \d+ assertion !__builtin_mul_overflow\(.*, .*, &big_result\): SUCCESS
46+
\[check_generic.assertion.7\] line \d+ assertion big_result == 4611686014132420609ll: SUCCESS
47+
\[check_generic.assertion.8\] line \d+ assertion __builtin_mul_overflow\(.* \* 2ll, .* \* 2ll, &big_result\): SUCCESS
48+
\[check_generic.assertion.9\] line \d+ assertion 0 && "reachability": FAILURE
4049
^EXIT=10$
4150
^SIGNAL=0$

regression/cbmc/gcc_builtin_sub_overflow/main.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ void check_unsigned_long_long(void)
7171
assert(0 && "reachability");
7272
}
7373

74+
void check_generic(void)
75+
{
76+
unsigned char small_result;
77+
long long big_result;
78+
79+
assert(__builtin_sub_overflow(5, 10, &small_result));
80+
assert(!__builtin_sub_overflow(5, 10, &big_result));
81+
assert(big_result == -5ll);
82+
assert(!__builtin_sub_overflow(10, 5, &small_result));
83+
assert(small_result == 5);
84+
assert(!__builtin_sub_overflow(10, 5, &big_result));
85+
assert(big_result == 5ll);
86+
assert(!__builtin_sub_overflow(INT_MIN, INT_MAX, &big_result));
87+
assert(big_result == 2ll * INT_MIN + 1);
88+
assert(0 && "reachability");
89+
}
90+
7491
int main(void)
7592
{
7693
check_int();
@@ -79,4 +96,5 @@ int main(void)
7996
check_unsigned();
8097
check_unsigned_long();
8198
check_unsigned_long_long();
99+
check_generic();
82100
}

regression/cbmc/gcc_builtin_sub_overflow/test.desc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,15 @@ main.c
4040
\[check_unsigned_long_long.assertion.5\] line \d+ assertion result == 0ull: SUCCESS
4141
\[check_unsigned_long_long.assertion.6\] line \d+ assertion __builtin_usubll_overflow\(.* / 2ull, .*, &result\): SUCCESS
4242
\[check_unsigned_long_long.assertion.7\] line \d+ assertion 0 && "reachability": FAILURE
43+
\[check_generic.assertion.1\] line 79 assertion __builtin_sub_overflow\(5, 10, &small_result\): SUCCESS
44+
\[check_generic.assertion.2\] line 80 assertion !__builtin_sub_overflow\(5, 10, &big_result\): SUCCESS
45+
\[check_generic.assertion.3\] line 81 assertion big_result == -5ll: SUCCESS
46+
\[check_generic.assertion.4\] line 82 assertion !__builtin_sub_overflow\(10, 5, &small_result\): SUCCESS
47+
\[check_generic.assertion.5\] line 83 assertion small_result == 5: SUCCESS
48+
\[check_generic.assertion.6\] line 84 assertion !__builtin_sub_overflow\(10, 5, &big_result\): SUCCESS
49+
\[check_generic.assertion.7\] line 85 assertion big_result == 5ll: SUCCESS
50+
\[check_generic.assertion.8\] line 86 assertion !__builtin_sub_overflow\(.*, .*, &big_result\): SUCCESS
51+
\[check_generic.assertion.9\] line 87 assertion big_result == 2ll \* .* \+ 1: SUCCESS
52+
\[check_generic.assertion.10\] line 88 assertion 0 && "reachability": FAILURE
4353
^EXIT=10$
4454
^SIGNAL=0$

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Daniel Kroening, [email protected]
1212
#include "c_typecheck_base.h"
1313

1414
#include <cassert>
15+
#include <sstream>
1516

1617
#include <util/arith_tools.h>
1718
#include <util/c_types.h>
@@ -33,6 +34,7 @@ Author: Daniel Kroening, [email protected]
3334
#include "builtin_factory.h"
3435
#include "c_qualifiers.h"
3536
#include "c_typecast.h"
37+
#include "expr2c.h"
3638
#include "padding.h"
3739
#include "type2name.h"
3840

@@ -2801,6 +2803,86 @@ exprt c_typecheck_baset::do_special_functions(
28012803
overflow.type() = bool_typet{};
28022804
return overflow;
28032805
}
2806+
else if(
2807+
identifier == "__builtin_add_overflow" ||
2808+
identifier == "__builtin_sub_overflow" ||
2809+
identifier == "__builtin_mul_overflow")
2810+
{
2811+
if(expr.arguments().size() != 3)
2812+
{
2813+
std::ostringstream error_message;
2814+
error_message << expr.source_location().as_string() << ": " << identifier
2815+
<< " takes exactly 3 arguments, but "
2816+
<< expr.arguments().size() << " were provided";
2817+
throw invalid_source_file_exceptiont{error_message.str()};
2818+
}
2819+
2820+
for(std::size_t arg_index = 0; arg_index < 3; ++arg_index)
2821+
{
2822+
auto const &argument = expr.arguments()[arg_index];
2823+
bool const arg_has_correct_type =
2824+
(arg_index < 2) ? (argument.type().id() == ID_signedbv ||
2825+
argument.type().id() == ID_unsignedbv)
2826+
: (argument.type().id() == ID_pointer &&
2827+
(argument.type().subtype().id() == ID_signedbv ||
2828+
argument.type().subtype().id() == ID_unsignedbv));
2829+
if(!arg_has_correct_type)
2830+
{
2831+
std::ostringstream error_message;
2832+
error_message << expr.source_location().as_string() << ": "
2833+
<< identifier << " has signature " << identifier
2834+
<< "(integral, integral, integral*), "
2835+
<< "but argument " << (arg_index + 1) << " ("
2836+
<< expr2c(argument, *this) << ") has type `"
2837+
<< type2c(argument.type(), *this) << '`';
2838+
throw invalid_source_file_exceptiont{error_message.str()};
2839+
}
2840+
}
2841+
2842+
auto lhs = expr.arguments()[0];
2843+
auto rhs = expr.arguments()[1];
2844+
auto result_ptr = expr.arguments()[2];
2845+
2846+
typecheck_expr(lhs);
2847+
typecheck_expr(rhs);
2848+
typecheck_expr(result_ptr);
2849+
2850+
auto const make_operation = [&identifier](exprt lhs, exprt rhs) -> exprt {
2851+
if(identifier == "__builtin_add_overflow")
2852+
{
2853+
return plus_exprt{lhs, rhs};
2854+
}
2855+
else if(identifier == "__builtin_sub_overflow")
2856+
{
2857+
return minus_exprt{lhs, rhs};
2858+
}
2859+
else
2860+
{
2861+
INVARIANT(
2862+
identifier == "__builtin_mul_overflow",
2863+
"the three overflow operations are add, sub and mul");
2864+
return mult_exprt{lhs, rhs};
2865+
}
2866+
};
2867+
2868+
auto operation = make_operation(
2869+
typecast_exprt{lhs, integer_typet{}},
2870+
typecast_exprt{rhs, integer_typet{}});
2871+
2872+
auto operation_result =
2873+
typecast_exprt{operation, result_ptr.type().subtype()};
2874+
typecheck_expr_typecast(operation_result);
2875+
auto overflow_check = notequal_exprt{
2876+
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
2877+
operation};
2878+
typecheck_expr(overflow_check);
2879+
return exprt{ID_comma,
2880+
bool_typet{},
2881+
{side_effect_expr_assignt{dereference_exprt{result_ptr},
2882+
operation,
2883+
expr.source_location()},
2884+
overflow_check}};
2885+
}
28042886
else
28052887
return nil_exprt();
28062888
}

0 commit comments

Comments
 (0)