Skip to content

Commit 45b1a1e

Browse files
committed
Use bitvector types to implement __builtin_{add,sub,mul}_overflow
The use of mathematical integers is not supported by the SAT back-end. Although GCC's specification describes the operations over mathematical integers, the same precision can be achieved by using bitvector types of sufficient width. The implementation now also introduces a fresh symbol to store the result of the full-precision operation. This ensures that the arguments to __builtin_{add,sub,mul}_overflow are evaluated exactly once.
1 parent 1739299 commit 45b1a1e

File tree

3 files changed

+57
-20
lines changed

3 files changed

+57
-20
lines changed

regression/cbmc/gcc_builtin_add_overflow/main.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ void check_generic(void)
110110
assert(0 && "reachability");
111111
}
112112

113+
void check_non_const()
114+
{
115+
int a, b, c, d, r;
116+
// this may overflow (negated assertion fails)
117+
assert(!__builtin_add_overflow(a, b, &r));
118+
// but need not overflow (assertion fails)
119+
assert(__builtin_add_overflow(c, d, &c));
120+
}
121+
113122
int main(void)
114123
{
115124
check_int();
@@ -119,4 +128,5 @@ int main(void)
119128
check_unsigned_long();
120129
check_unsigned_long_long();
121130
check_generic();
131+
check_non_const();
122132
}

regression/cbmc/gcc_builtin_add_overflow/test.desc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ 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_non_const\.assertion\.1\] line \d+ assertion !__builtin_add_overflow\(a, b, &r\): FAILURE
61+
\[check_non_const\.assertion\.2\] line \d+ assertion __builtin_add_overflow\(c, d, &c\): FAILURE
6062
VERIFICATION FAILED
6163
^EXIT=10$
6264
^SIGNAL=0$

src/goto-programs/goto_convert_side_effect.cpp

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -601,46 +601,71 @@ void goto_convertt::remove_overflow(
601601
const exprt &rhs = expr.rhs();
602602
const exprt &result_ptr = expr.result();
603603

604-
// actual logic implementing the operators
604+
// actual logic implementing the operators: perform operations on signed
605+
// bitvector types of sufficiently large size to hold the result
605606
auto const make_operation = [&statement](exprt lhs, exprt rhs) -> exprt {
607+
std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width();
608+
if(lhs.type().id() == ID_unsignedbv)
609+
++lhs_ssize;
610+
std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width();
611+
if(rhs.type().id() == ID_unsignedbv)
612+
++rhs_ssize;
613+
606614
if(statement == ID_overflow_plus)
607615
{
608-
return plus_exprt{lhs, rhs};
616+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
617+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
618+
return plus_exprt{typecast_exprt{lhs, ssize_type},
619+
typecast_exprt{rhs, ssize_type}};
609620
}
610621
else if(statement == ID_overflow_minus)
611622
{
612-
return minus_exprt{lhs, rhs};
623+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
624+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
625+
return minus_exprt{typecast_exprt{lhs, ssize_type},
626+
typecast_exprt{rhs, ssize_type}};
613627
}
614628
else
615629
{
616630
INVARIANT(
617631
statement == ID_overflow_mult,
618632
"the three overflow operations are add, sub and mul");
619-
return mult_exprt{lhs, rhs};
633+
std::size_t ssize = lhs_ssize + rhs_ssize;
634+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
635+
return mult_exprt{typecast_exprt{lhs, ssize_type},
636+
typecast_exprt{rhs, ssize_type}};
620637
}
621638
};
622639

623-
// we’re basically generating this expression
624-
// (*result = (result_type)((integer)lhs OP (integer)rhs)),
625-
// ((integer)result == (integer)lhs OP (integer)rhs)
626-
// i.e. perform the operation (+, -, *) on arbitrary length integer,
627-
// cast to result type, check if the casted result is still equivalent
628-
// to the arbitrary length result.
629-
auto operation = make_operation(
630-
typecast_exprt{lhs, integer_typet{}}, typecast_exprt{rhs, integer_typet{}});
631-
632-
typecast_exprt operation_result{operation, result_ptr.type().subtype()};
633-
634-
code_assignt assign{dereference_exprt{result_ptr},
635-
std::move(operation_result),
636-
expr.source_location()};
637-
convert_assign(assign, dest, mode);
640+
// Generating the following sequence of statements:
641+
// large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs;
642+
// *result = (result_type)tmp;
643+
// (large_signedbv)*result != tmp;
644+
// This performs the operation (+, -, *) on a signed bitvector type of
645+
// sufficiently large width to store the precise result, cast to result
646+
// type, check if the cast result is not equivalent to the full-length
647+
// result.
648+
auto operation = make_operation(lhs, rhs);
649+
// Disable overflow checks as the operation cannot overflow on the larger
650+
// type
651+
operation.add_source_location() = expr.source_location();
652+
operation.add_source_location().add_pragma("disable:signed-overflow-check");
653+
654+
make_temp_symbol(operation, "large_bv", dest, mode);
655+
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);
638661

639662
if(result_is_used)
640663
{
641664
notequal_exprt overflow_check{
642-
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
665+
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
643666
operation};
667+
overflow_check.add_source_location() = expr.source_location();
668+
644669
expr.swap(overflow_check);
645670
}
646671
else

0 commit comments

Comments
 (0)