Skip to content

Commit f930567

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 ef796ac commit f930567

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
@@ -599,46 +599,71 @@ void goto_convertt::remove_overflow(
599599
const exprt &rhs = expr.b();
600600
const exprt &result_ptr = expr.result();
601601

602-
// actual logic implementing the operators
602+
// actual logic implementing the operators: perform operations on signed
603+
// bitvector types of sufficiently large size to hold the result
603604
auto const make_operation = [&statement](exprt lhs, exprt rhs) -> exprt {
605+
std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width();
606+
if(lhs.type().id() == ID_unsignedbv)
607+
++lhs_ssize;
608+
std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width();
609+
if(rhs.type().id() == ID_unsignedbv)
610+
++rhs_ssize;
611+
604612
if(statement == ID_overflow_plus)
605613
{
606-
return plus_exprt{lhs, rhs};
614+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
615+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
616+
return plus_exprt{typecast_exprt{lhs, ssize_type},
617+
typecast_exprt{rhs, ssize_type}};
607618
}
608619
else if(statement == ID_overflow_minus)
609620
{
610-
return minus_exprt{lhs, rhs};
621+
std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1;
622+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
623+
return minus_exprt{typecast_exprt{lhs, ssize_type},
624+
typecast_exprt{rhs, ssize_type}};
611625
}
612626
else
613627
{
614628
INVARIANT(
615629
statement == ID_overflow_mult,
616630
"the three overflow operations are add, sub and mul");
617-
return mult_exprt{lhs, rhs};
631+
std::size_t ssize = lhs_ssize + rhs_ssize;
632+
integer_bitvector_typet ssize_type{ID_signedbv, ssize};
633+
return mult_exprt{typecast_exprt{lhs, ssize_type},
634+
typecast_exprt{rhs, ssize_type}};
618635
}
619636
};
620637

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

637660
if(result_is_used)
638661
{
639662
notequal_exprt overflow_check{
640-
typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}},
663+
typecast_exprt{dereference_exprt{result_ptr}, operation.type()},
641664
operation};
665+
overflow_check.add_source_location() = expr.source_location();
666+
642667
expr.swap(overflow_check);
643668
}
644669
else

0 commit comments

Comments
 (0)