Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle some special cases of bitand in invariant and Interval, DefExc, and Congruences #1154

Merged
merged 3 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions src/analyses/baseInvariant.ml
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,18 @@ struct
meet_bin c c
else
a, b
| BAnd as op ->
(* we only attempt to refine a here *)
let a =
match ID.to_int b with
| Some x when BI.equal x BI.one ->
(match ID.to_bool c with
| Some true -> ID.meet a (ID.of_congruence ikind (Z.one, Z.of_int 2))
| Some false -> ID.meet a (ID.of_congruence ikind (Z.zero, Z.of_int 2))
| None -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a 1 = %a\n" d_binop op ID.pretty c; a)
| _ -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a %a = %a\n" d_binop op ID.pretty b ID.pretty c; a
in
a, b
| op ->
if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op;
a, b
Expand Down Expand Up @@ -545,8 +557,8 @@ struct
in
let eval e st = eval_rv a gs st e in
let eval_bool e st = match eval e st with Int i -> ID.to_bool i | _ -> None in
let unroll_fk_of_exp e =
match unrollType (Cilfacade.typeOf e) with
let unroll_fk_of_exp e =
match unrollType (Cilfacade.typeOf e) with
| TFloat (fk, _) -> fk
| _ -> failwith "value which was expected to be a float is of different type?!"
in
Expand Down Expand Up @@ -700,8 +712,8 @@ struct
begin match t with
| TInt (ik, _) ->
begin match x with
| ((Var v), offs) ->
if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)));
| ((Var v), offs) ->
if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)));
let tv_opt = ID.to_bool c in
begin match tv_opt with
| Some tv ->
Expand Down Expand Up @@ -735,12 +747,12 @@ struct
begin match t with
| TFloat (fk, _) ->
begin match x with
| ((Var v), offs) ->
if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)));
| ((Var v), offs) ->
if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)));
begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with
| `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st
| `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st
| `Lifted (Fabs (ret_fk, xFloat)) ->
| `Lifted (Fabs (ret_fk, xFloat)) ->
let inv = FD.inv_fabs (FD.cast_to ret_fk c) in
if FD.is_bot inv then
raise Analyses.Deadcode
Expand Down
58 changes: 51 additions & 7 deletions src/cdomains/intDomain.ml
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ module Size = struct (* size in bits as int, range as int64 *)

let cast t x = (* TODO: overflow is implementation-dependent! *)
if t = IBool then
(* C11 6.3.1.2 Boolean type *)
(* C11 6.3.1.2 Boolean type *)
if Z.equal x Z.zero then Z.zero else Z.one
else
let a,b = range t in
Expand Down Expand Up @@ -826,7 +826,18 @@ struct
| _ -> (top_of ik,{underflow=true; overflow=true})

let bitxor = bit (fun _ik -> Ints_t.bitxor)
let bitand = bit (fun _ik -> Ints_t.bitand)

let bitand ik i1 i2 =
match is_bot i1, is_bot i2 with
| true, true -> bot_of ik
| true, _
| _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2)))
| _ ->
match to_int i1, to_int i2 with
| Some x, Some y -> (try of_int ik (Ints_t.bitand x y) |> fst with Division_by_zero -> top_of ik)
| _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst
| _ -> top_of ik

let bitor = bit (fun _ik -> Ints_t.bitor)

let bit1 f ik i1 =
Expand Down Expand Up @@ -1978,15 +1989,15 @@ struct
let top_of ik = `Excluded (S.empty (), size ik)
let cast_to ?torg ?no_ov ik = function
| `Excluded (s,r) ->
let r' = size ik in
let r' = size ik in
if R.leq r r' then (* upcast -> no change *)
`Excluded (s, r)
else if ik = IBool then (* downcast to bool *)
if S.mem BI.zero s then
`Definite (BI.one)
else
`Excluded (S.empty(), r')
else
else
(* downcast: may overflow *)
(* let s' = S.map (BigInt.cast_to ik) s in *)
(* We want to filter out all i in s' where (t)x with x in r could be i. *)
Expand Down Expand Up @@ -2282,7 +2293,28 @@ struct
let ge ik x y = le ik y x

let bitnot = lift1 BigInt.bitnot
let bitand = lift2 BigInt.bitand

let bitand ik x y = norm ik (match x,y with
(* We don't bother with exclusion sets: *)
| `Excluded _, `Definite i ->
(* Except in two special cases *)
if BigInt.equal i BigInt.zero then
`Definite BigInt.zero
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved
else if BigInt.equal i BigInt.one then
of_interval IBool (BigInt.zero, BigInt.one)
sim642 marked this conversation as resolved.
Show resolved Hide resolved
else
top ()
| `Definite _, `Excluded _
| `Excluded _, `Excluded _ -> top ()
(* The good case: *)
| `Definite x, `Definite y ->
(try `Definite (BigInt.bitand x y) with | Division_by_zero -> top ())
| `Bot, `Bot -> `Bot
| _ ->
(* If only one of them is bottom, we raise an exception that eval_rv will catch *)
raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))))


let bitor = lift2 BigInt.bitor
let bitxor = lift2 BigInt.bitxor

Expand Down Expand Up @@ -3134,7 +3166,7 @@ struct

(** The implementation of the bit operations could be improved based on the master’s thesis
'Abstract Interpretation and Abstract Domains' written by Stefan Bygde.
see: https://www.dsi.unive.it/~avp/domains.pdf *)
see: http://www.es.mdh.se/pdf_publications/948.pdf *)
let bit2 f ik x y = match x, y with
| None, None -> None
| None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))
Expand All @@ -3144,7 +3176,19 @@ struct

let bitor ik x y = bit2 Ints_t.bitor ik x y

let bitand ik x y = bit2 Ints_t.bitand ik x y
let bitand ik x y = match x, y with
| None, None -> None
| None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))
| Some (c, m), Some (c', m') ->
if (m =: Ints_t.zero && m' =: Ints_t.zero) then
(* both arguments constant *)
Some (Ints_t.bitand c c', Ints_t.zero)
else if m' =: Ints_t.zero && c' =: Ints_t.one && Ints_t.rem m (Ints_t.of_int 2) =: Ints_t.zero then
(* x & 1 and x == c (mod 2*z) *)
(* Value is equal to LSB of c *)
Some (Ints_t.bitand c c', Ints_t.zero)
sim642 marked this conversation as resolved.
Show resolved Hide resolved
else
top ()

let bitxor ik x y = bit2 Ints_t.bitxor ik x y

Expand Down
40 changes: 40 additions & 0 deletions tests/regression/37-congruence/13-bitand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// PARAM: --enable ana.int.congruence --set sem.int.signed_overflow assume_none
#include <goblint.h>

int main()
{
// Assuming modulo expression

long x;
__goblint_assume(x % 2 == 1);
__goblint_check(x % 2 == 1);
__goblint_check(x & 1);

long y;
__goblint_assume(y % 2 == 0);
__goblint_check(y % 2 == 0);
__goblint_check(y & 1); //FAIL

long z;
__goblint_check(z & 1); //UNKNOWN!
__goblint_assume(z % 8 == 1);
__goblint_check(z & 1);

long xz;
__goblint_assume(xz % 3 == 1);
__goblint_check(xz & 1); //UNKNOWN!
__goblint_assume(xz % 6 == 1);
__goblint_check(xz & 1);

// Assuming bitwise expression

long a;
__goblint_assume(a & 1);
__goblint_check(a % 2 == 1);
__goblint_check(a & 1);

int b;
__goblint_assume(b & 1);
__goblint_check(b % 2 == 1);
__goblint_check(b & 1);
}
Loading