|
19 | 19 | #include <util/cprover_prefix.h>
|
20 | 20 | #include <util/expr_util.h>
|
21 | 21 | #include <util/floatbv_expr.h>
|
| 22 | +#include <util/fresh_symbol.h> |
22 | 23 | #include <util/ieee_float.h>
|
23 | 24 | #include <util/mathematical_expr.h>
|
24 | 25 | #include <util/mathematical_types.h>
|
@@ -3014,48 +3015,85 @@ exprt c_typecheck_baset::do_special_functions(
|
3014 | 3015 | }
|
3015 | 3016 | }
|
3016 | 3017 |
|
3017 |
| - // actual logic implementing the operators |
| 3018 | + // actual logic implementing the operators: perform operations on signed |
| 3019 | + // bitvector types of sufficiently large size to hold the result |
3018 | 3020 | auto const make_operation = [&identifier](exprt lhs, exprt rhs) -> exprt {
|
| 3021 | + std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width(); |
| 3022 | + if(lhs.type().id() == ID_unsignedbv) |
| 3023 | + ++lhs_ssize; |
| 3024 | + std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width(); |
| 3025 | + if(rhs.type().id() == ID_unsignedbv) |
| 3026 | + ++rhs_ssize; |
| 3027 | + |
3019 | 3028 | if(identifier == "__builtin_add_overflow")
|
3020 | 3029 | {
|
3021 |
| - return plus_exprt{lhs, rhs}; |
| 3030 | + std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1; |
| 3031 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 3032 | + return plus_exprt{typecast_exprt{lhs, ssize_type}, |
| 3033 | + typecast_exprt{rhs, ssize_type}}; |
3022 | 3034 | }
|
3023 | 3035 | else if(identifier == "__builtin_sub_overflow")
|
3024 | 3036 | {
|
3025 |
| - return minus_exprt{lhs, rhs}; |
| 3037 | + std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1; |
| 3038 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 3039 | + return minus_exprt{typecast_exprt{lhs, ssize_type}, |
| 3040 | + typecast_exprt{rhs, ssize_type}}; |
3026 | 3041 | }
|
3027 | 3042 | else
|
3028 | 3043 | {
|
3029 | 3044 | INVARIANT(
|
3030 | 3045 | identifier == "__builtin_mul_overflow",
|
3031 | 3046 | "the three overflow operations are add, sub and mul");
|
3032 |
| - return mult_exprt{lhs, rhs}; |
| 3047 | + std::size_t ssize = lhs_ssize + rhs_ssize; |
| 3048 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 3049 | + return mult_exprt{typecast_exprt{lhs, ssize_type}, |
| 3050 | + typecast_exprt{rhs, ssize_type}}; |
3033 | 3051 | }
|
3034 | 3052 | };
|
3035 | 3053 |
|
3036 |
| - // we’re basically generating this expression |
3037 |
| - // (*result = (result_type)((integer)lhs OP (integer)rhs)), |
3038 |
| - // ((integer)result == (integer)lhs OP (integer)rhs) |
3039 |
| - // i.e. perform the operation (+, -, *) on arbitrary length integer, |
3040 |
| - // cast to result type, check if the casted result is still equivalent |
3041 |
| - // to the arbitrary length result. |
3042 |
| - auto operation = make_operation( |
3043 |
| - typecast_exprt{lhs, integer_typet{}}, |
3044 |
| - typecast_exprt{rhs, integer_typet{}}); |
3045 |
| - |
3046 |
| - auto operation_result = |
3047 |
| - typecast_exprt{operation, result_ptr.type().subtype()}; |
3048 |
| - typecheck_expr_typecast(operation_result); |
3049 |
| - auto overflow_check = notequal_exprt{ |
3050 |
| - typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}}, |
3051 |
| - operation}; |
3052 |
| - typecheck_expr(overflow_check); |
3053 |
| - return exprt{ID_comma, |
3054 |
| - bool_typet{}, |
3055 |
| - {side_effect_expr_assignt{dereference_exprt{result_ptr}, |
3056 |
| - operation_result, |
3057 |
| - expr.source_location()}, |
3058 |
| - overflow_check}}; |
| 3054 | + // Generating the following statement expression: |
| 3055 | + // ({ large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs; |
| 3056 | + // *result = (result_type)large_signedbv; |
| 3057 | + // (large_signedbv)*result != tmp; }) |
| 3058 | + // This performs the operation (+, -, *) on a signed bitvector type of |
| 3059 | + // sufficiently large width to store the precise result, cast to result |
| 3060 | + // type, check if the casted result is not equivalent to the full-length |
| 3061 | + // result. |
| 3062 | + auto operation = make_operation(lhs, rhs); |
| 3063 | + // Disable overflow checks as the operation cannot overflow on the larger |
| 3064 | + // type |
| 3065 | + operation.add_source_location() = expr.source_location(); |
| 3066 | + operation.add_source_location().add_pragma("disable:signed-overflow-check"); |
| 3067 | + |
| 3068 | + const symbolt &new_symbol = get_fresh_aux_symbol( |
| 3069 | + operation.type(), |
| 3070 | + id2string(current_symbol.name) + "$tmp", |
| 3071 | + id2string(current_symbol.base_name) + "$tmp", |
| 3072 | + expr.source_location(), |
| 3073 | + mode, |
| 3074 | + symbol_table); |
| 3075 | + |
| 3076 | + code_declt decl{new_symbol.symbol_expr()}; |
| 3077 | + decl.add_source_location() = expr.source_location(); |
| 3078 | + |
| 3079 | + code_assignt tmp_assignment{decl.symbol(), operation}; |
| 3080 | + tmp_assignment.add_source_location() = expr.source_location(); |
| 3081 | + |
| 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(); |
| 3086 | + |
| 3087 | + notequal_exprt overflow_check{ |
| 3088 | + typecast_exprt{dereference_exprt{result_ptr}, operation.type()}, |
| 3089 | + decl.symbol()}; |
| 3090 | + overflow_check.add_source_location() = expr.source_location(); |
| 3091 | + code_expressiont return_expr{overflow_check}; |
| 3092 | + |
| 3093 | + code_blockt statements{ |
| 3094 | + {decl, tmp_assignment, result_assignment, return_expr}}; |
| 3095 | + return side_effect_expr_statement_expressiont{ |
| 3096 | + statements, overflow_check.type(), expr.source_location()}; |
3059 | 3097 | }
|
3060 | 3098 | else
|
3061 | 3099 | return nil_exprt();
|
|
0 commit comments