Skip to content

Commit

Permalink
Merge pull request #1344 from goblint/issue_1211
Browse files Browse the repository at this point in the history
Handle `LNot` for float in forward evaluation and refinement on guards
  • Loading branch information
michael-schwarz authored Feb 4, 2024
2 parents e8da916 + c8b2fb7 commit 71498c5
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 26 deletions.
15 changes: 11 additions & 4 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,15 @@ struct
| LNot -> ID.c_lognot

let unop_FD = function
| Neg -> FD.neg
(* other unary operators are not implemented on float values *)
| _ -> (fun c -> FD.top_of (FD.get_fkind c))
| Neg -> (fun v -> (Float (FD.neg v):value))
| LNot -> (fun c -> Int (FD.eq c (FD.of_const (FD.get_fkind c) 0.)))
| BNot -> failwith "BNot on a value of type float!"


(* Evaluating Cil's unary operators. *)
let evalunop op typ: value -> value = function
| Int v1 -> Int (ID.cast_to (Cilfacade.get_ikind typ) (unop_ID op v1))
| Float v -> Float (unop_FD op v)
| Float v -> unop_FD op v
| Address a when op = LNot ->
if AD.is_null a then
Int (ID.of_bool (Cilfacade.get_ikind typ) true)
Expand Down Expand Up @@ -1644,6 +1645,9 @@ struct
module V = V
module G = G

let unop_ID = unop_ID
let unop_FD = unop_FD

let eval_rv = eval_rv
let eval_rv_address = eval_rv_address
let eval_lv = eval_lv
Expand Down Expand Up @@ -2841,6 +2845,9 @@ struct

let ost = octx.local

let unop_ID = unop_ID
let unop_FD = unop_FD

(* all evals happen in octx with non-top values *)
let eval_rv ~ctx st e = eval_rv ~ctx:octx ost e
let eval_rv_address ~ctx st e = eval_rv_address ~ctx:octx ost e
Expand Down
50 changes: 28 additions & 22 deletions src/analyses/baseInvariant.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ sig
module V: Analyses.SpecSysVar
module G: Lattice.S

val unop_ID: Cil.unop -> ID.t -> ID.t
val unop_FD: Cil.unop -> FD.t -> VD.t

val eval_rv: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> exp -> VD.t
val eval_rv_address: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> exp -> VD.t
val eval_lv: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> lval -> AD.t
Expand Down Expand Up @@ -41,16 +44,6 @@ module Make (Eval: Eval) =
struct
open Eval

let unop_ID = function
| Neg -> ID.neg
| BNot -> ID.lognot
| LNot -> ID.c_lognot

let unop_FD = function
| Neg -> FD.neg
(* other unary operators are not implemented on float values *)
| _ -> (fun c -> FD.top_of (FD.get_fkind c))

let is_some_bot (x:VD.t) =
match x with
| Bot -> false (* HACK: bot is here due to typing conflict (we do not cast appropriately) *)
Expand Down Expand Up @@ -565,18 +558,31 @@ struct
else
match exp, c_typed with
| UnOp (LNot, e, _), Int c ->
let ikind = Cilfacade.get_ikind_exp e in
let c' =
match ID.to_bool (unop_ID LNot c) with
| Some true ->
(* i.e. e should evaluate to [1,1] *)
(* LNot x is 0 for any x != 0 *)
ID.of_excl_list ikind [Z.zero]
| Some false -> ID.of_bool ikind false
| _ -> ID.top_of ikind
in
inv_exp (Int c') e st
| UnOp (Neg, e, _), Float c -> inv_exp (Float (unop_FD Neg c)) e st
(match Cilfacade.typeOf e with
| TInt _ | TPtr _ ->
let ikind = Cilfacade.get_ikind_exp e in
let c' =
match ID.to_bool (unop_ID LNot c) with
| Some true ->
(* i.e. e should evaluate to [1,1] *)
(* LNot x is 0 for any x != 0 *)
ID.of_excl_list ikind [Z.zero]
| Some false -> ID.of_bool ikind false
| _ -> ID.top_of ikind
in
inv_exp (Int c') e st
| TFloat(fkind, _) when ID.to_bool (unop_ID LNot c) = Some false ->
(* C99 §6.5.3.3/5 *)
(* The result of the logical negation operator ! is 0 if the value of its operand compares *)
(* unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. *)
(* The expression !E is equivalent to (0==E). *)
(* NaN compares unequal to 0 so no problems *)
(* We do not have exclusions for floats, so we do not bother here with the other case *)
let zero_float = FD.of_const fkind 0. in
inv_exp (Float zero_float) e st
| _ -> st
)
| UnOp (Neg, e, _), Float c -> inv_exp (unop_FD Neg c) e st
| UnOp ((BNot|Neg) as op, e, _), Int c -> inv_exp (Int (unop_ID op c)) e st
(* no equivalent for Float, as VD.is_safe_cast fails for all float types anyways *)
| BinOp((Eq | Ne) as op, CastE (t1, e1), CastE (t2, e2), t), Int c when typeSig (Cilfacade.typeOf e1) = typeSig (Cilfacade.typeOf e2) && VD.is_safe_cast t1 (Cilfacade.typeOf e1) && VD.is_safe_cast t2 (Cilfacade.typeOf e2) ->
Expand Down
26 changes: 26 additions & 0 deletions tests/regression/57-floats/22-lnot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//PARAM: --enable ana.float.interval
#include<goblint.h>
int main() {
float x = 0.0f;
int z = !x;

int reach;

if(z) {
__goblint_check(1); //Reachable
reach = 1;
} else {
reach = 0;
}

__goblint_check(reach == 1);

float y;
if (!y) {
__goblint_check(y == 0.0f);
} else {
__goblint_check(1); //Reachable
}

return 0;
}

0 comments on commit 71498c5

Please sign in to comment.