|
19 | 19 | #include <util/config.h>
|
20 | 20 | #include <util/cprover_prefix.h>
|
21 | 21 | #include <util/expr_util.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>
|
@@ -2862,48 +2863,85 @@ exprt c_typecheck_baset::do_special_functions(
|
2862 | 2863 | }
|
2863 | 2864 | }
|
2864 | 2865 |
|
2865 |
| - // actual logic implementing the operators |
| 2866 | + // actual logic implementing the operators: perform operations on signed |
| 2867 | + // bitvector types of sufficiently large size to hold the result |
2866 | 2868 | auto const make_operation = [&identifier](exprt lhs, exprt rhs) -> exprt {
|
| 2869 | + std::size_t lhs_ssize = to_bitvector_type(lhs.type()).get_width(); |
| 2870 | + if(lhs.type().id() == ID_unsignedbv) |
| 2871 | + ++lhs_ssize; |
| 2872 | + std::size_t rhs_ssize = to_bitvector_type(rhs.type()).get_width(); |
| 2873 | + if(rhs.type().id() == ID_unsignedbv) |
| 2874 | + ++rhs_ssize; |
| 2875 | + |
2867 | 2876 | if(identifier == "__builtin_add_overflow")
|
2868 | 2877 | {
|
2869 |
| - return plus_exprt{lhs, rhs}; |
| 2878 | + std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1; |
| 2879 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 2880 | + return plus_exprt{typecast_exprt{lhs, ssize_type}, |
| 2881 | + typecast_exprt{rhs, ssize_type}}; |
2870 | 2882 | }
|
2871 | 2883 | else if(identifier == "__builtin_sub_overflow")
|
2872 | 2884 | {
|
2873 |
| - return minus_exprt{lhs, rhs}; |
| 2885 | + std::size_t ssize = std::max(lhs_ssize, rhs_ssize) + 1; |
| 2886 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 2887 | + return minus_exprt{typecast_exprt{lhs, ssize_type}, |
| 2888 | + typecast_exprt{rhs, ssize_type}}; |
2874 | 2889 | }
|
2875 | 2890 | else
|
2876 | 2891 | {
|
2877 | 2892 | INVARIANT(
|
2878 | 2893 | identifier == "__builtin_mul_overflow",
|
2879 | 2894 | "the three overflow operations are add, sub and mul");
|
2880 |
| - return mult_exprt{lhs, rhs}; |
| 2895 | + std::size_t ssize = lhs_ssize + rhs_ssize; |
| 2896 | + integer_bitvector_typet ssize_type{ID_signedbv, ssize}; |
| 2897 | + return mult_exprt{typecast_exprt{lhs, ssize_type}, |
| 2898 | + typecast_exprt{rhs, ssize_type}}; |
2881 | 2899 | }
|
2882 | 2900 | };
|
2883 | 2901 |
|
2884 |
| - // we’re basically generating this expression |
2885 |
| - // (*result = (result_type)((integer)lhs OP (integer)rhs)), |
2886 |
| - // ((integer)result == (integer)lhs OP (integer)rhs) |
2887 |
| - // i.e. perform the operation (+, -, *) on arbitrary length integer, |
2888 |
| - // cast to result type, check if the casted result is still equivalent |
2889 |
| - // to the arbitrary length result. |
2890 |
| - auto operation = make_operation( |
2891 |
| - typecast_exprt{lhs, integer_typet{}}, |
2892 |
| - typecast_exprt{rhs, integer_typet{}}); |
2893 |
| - |
2894 |
| - auto operation_result = |
2895 |
| - typecast_exprt{operation, result_ptr.type().subtype()}; |
2896 |
| - typecheck_expr_typecast(operation_result); |
2897 |
| - auto overflow_check = notequal_exprt{ |
2898 |
| - typecast_exprt{dereference_exprt{result_ptr}, integer_typet{}}, |
2899 |
| - operation}; |
2900 |
| - typecheck_expr(overflow_check); |
2901 |
| - return exprt{ID_comma, |
2902 |
| - bool_typet{}, |
2903 |
| - {side_effect_expr_assignt{dereference_exprt{result_ptr}, |
2904 |
| - operation_result, |
2905 |
| - expr.source_location()}, |
2906 |
| - overflow_check}}; |
| 2902 | + // Generating the following statement expression: |
| 2903 | + // ({ large_signedbv tmp = (large_signedbv)lhs OP (large_signedbv)rhs; |
| 2904 | + // *result = (result_type)large_signedbv; |
| 2905 | + // (large_signedbv)*result != tmp; }) |
| 2906 | + // This performs the operation (+, -, *) on a signed bitvector type of |
| 2907 | + // sufficiently large width to store the precise result, cast to result |
| 2908 | + // type, check if the casted result is not equivalent to the full-length |
| 2909 | + // result. |
| 2910 | + auto operation = make_operation(lhs, rhs); |
| 2911 | + // Disable overflow checks as the operation cannot overflow on the larger |
| 2912 | + // type |
| 2913 | + operation.add_source_location() = expr.source_location(); |
| 2914 | + operation.add_source_location().add_pragma("disable:signed-overflow-check"); |
| 2915 | + |
| 2916 | + const symbolt &new_symbol = get_fresh_aux_symbol( |
| 2917 | + operation.type(), |
| 2918 | + id2string(current_symbol.name) + "$tmp", |
| 2919 | + id2string(current_symbol.base_name) + "$tmp", |
| 2920 | + expr.source_location(), |
| 2921 | + mode, |
| 2922 | + symbol_table); |
| 2923 | + |
| 2924 | + code_declt decl{new_symbol.symbol_expr()}; |
| 2925 | + decl.add_source_location() = expr.source_location(); |
| 2926 | + |
| 2927 | + code_assignt tmp_assignment{decl.symbol(), operation}; |
| 2928 | + tmp_assignment.add_source_location() = expr.source_location(); |
| 2929 | + |
| 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(); |
| 2934 | + |
| 2935 | + notequal_exprt overflow_check{ |
| 2936 | + typecast_exprt{dereference_exprt{result_ptr}, operation.type()}, |
| 2937 | + decl.symbol()}; |
| 2938 | + overflow_check.add_source_location() = expr.source_location(); |
| 2939 | + code_expressiont return_expr{overflow_check}; |
| 2940 | + |
| 2941 | + code_blockt statements{ |
| 2942 | + {decl, tmp_assignment, result_assignment, return_expr}}; |
| 2943 | + return side_effect_expr_statement_expressiont{ |
| 2944 | + statements, overflow_check.type(), expr.source_location()}; |
2907 | 2945 | }
|
2908 | 2946 | else
|
2909 | 2947 | return nil_exprt();
|
|
0 commit comments