let get_multsubadd_assertion
    ~simplify_constants:simplify_constants
    ~warning:warning
    full_expr op expr1 expr2 =
  (* signed multiplication/addition/subtraction:
     the expression overflows iff its integer value
     is strictly more than TYPE_MAX or strictly less than TYPE_MIN *)

  let t = Cil.typeOf full_expr in
  let size = bitsSizeOf t
  in if (size > 64) then (
      (* should never happen *)
      rte_warn "bitsSize of %a > 64: not treated" d_exp full_expr ;
      []
    )
    else
      let (minType,maxType) = (get_signed_min size, get_signed_max size) in
      let full_add_term () =
        (* no cast to int since we check "true result" which is an integer *)
        let term1 = translate_C_expr_to_term ~cast:false expr1
        and term2 = translate_C_expr_to_term ~cast:false expr2
        in Logic_const.term (TBinOp (op, term1,term2)) (Ctype t)
      in
      let assertion_le ()   = assertion_le (full_add_term ()) maxType
      and assertion_ge ()   = assertion_ge (full_add_term ()) minType in
      let full_assertion () = Logic_const.pand (assertion_le (), assertion_ge ())
      in
        if simplify_constants then (
          match get_expr_val expr1, get_expr_val expr2 with
            | Some a64, Some b64 -> (* both operands are constant *)
                let big_a64 = Int.of_int64 a64
                and big_b64 = Int.of_int64 b64
                in
                  if op = MinusA then
                    let big_diff = Int.sub big_a64 big_b64
                    in if Int.compare big_diff (Int.of_int64 minType) < 0 then
                        let assertion = assertion_ge ()
                        in
                          if warning then
                            rte_warn
                              "signed overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else [ ]
                  else if op = PlusA then
                    let big_add = Int.add big_a64 big_b64
                    in if Int.compare big_add (Int.of_int64 maxType) > 0 then
                        let assertion = assertion_le ()
                        in
                          if warning then
                            rte_warn
                              "signed overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else [ ]
                  else (
                    assert(op = Mult) ;
                    let big_mult = Int.mul big_a64 big_b64
                    in let b_ov = (Int.compare big_mult (Int.of_int64 maxType) > 0)
                    in if b_ov then
                        let assertion = assertion_le ()
                        in
                          if warning then
                            rte_warn
                              "signed overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else let b_uv = (Int.compare big_mult (Int.of_int64 minType) < 0)
                      in if b_uv then
                        let assertion = assertion_ge ()
                        in
                          if warning then
                            rte_warn
                              "signed overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else [ ]
                  )
            | Some a64, None
            | NoneSome a64 ->  (* one operand is constant *)
                if op = MinusA then [ (assertion_ge (), None) ]
                else if op = PlusA then [ (assertion_le (), None) ]
                else (
                  assert(op = Mult);
                  (* multiplying by 1 or 0 if not dangerous *)
                  if (Int64.compare a64 Int64.zero = 0) ||
                    (Int64.compare a64 Int64.one = 0) then []
                  else
                    (* multiplying by -1 is dangerous (albeit seldom) *)
                    if (Int64.compare a64 (Int64.of_int (-1)) = 0) then
                      [ (assertion_le (), None) ]
                    else [ (full_assertion (), None) ]
                )
            | None,None -> [ (full_assertion (), None) ] (* no operand is a constant *)
        ) else [ (full_assertion (), None) ]