Skip to content

Commit

Permalink
rough outline of refinement
Browse files Browse the repository at this point in the history
  • Loading branch information
DrMichaelPetter committed Nov 28, 2024
1 parent c2db27d commit c078c2a
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 6 deletions.
15 changes: 15 additions & 0 deletions src/analyses/apron/linearTwoVarEqualityAnalysis.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ let spec_module: (module MCPSpec) Lazy.t =
struct
include SpecFunctor (Priv) (AD) (RelationPrecCompareUtil.DummyUtil)
let name () = "lin2vareq"
let branch ctx e b =
if M.tracing then M.trace "lin2vareq" "Branching";
let res = branch ctx e b in
let st = ctx.local in
let ask = Analyses.ask_of_ctx ctx in
let _ = assign_from_globals_wrapper ask ctx.global st e (fun d e' ->
try
let tcons = AD.tcons1_of_cil_exp ask d d.env e (not b) (no_overflow ask e) in
let constraintlist = AD.refine_value_domains ctx d tcons in
List.iter (fun e -> ctx.emit (Events.Assert e)) constraintlist;
d
with _ -> d)
in
res

end
in
(module Spec)
Expand Down
84 changes: 78 additions & 6 deletions src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,18 @@ module EqualitiesConjunction = struct
if M.tracing then M.tracel "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd res))
; res

let get_equivalent_expressions econ i =
let ropt,_,_ = get_rhs econ i in
let clusterrefvar = snd @@ BatOption.get ropt in
List.filter (fun (_,(refvaropt,_,_)) -> BatOption.map_default (fun (_,x) -> x = clusterrefvar) true refvaropt) (IntMap.bindings (snd econ)) (* obtain all variables in the equivalence clas *)

let get_equivalent_expressions econ i = timing_wrap "get_eq_expressions" (get_equivalent_expressions econ) i

let get_equivalent_expressions econ i =
let res = get_equivalent_expressions econ i in
if M.tracing then M.tracel "equivalencegroup" "get_eq_expressions var_%d : { %s }" i (String.concat ", " @@ List.map (fun (i,rhs) -> Printf.sprintf "var_%d = %s" i (Rhs.show rhs)) res);
res

(** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *)
let affine_transform econ i (coeff, j, offs, divi) =
if nontrivial econ i then (* i cannot occur on any other rhs apart from itself *)
Expand Down Expand Up @@ -698,6 +710,65 @@ struct

let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov

let refine_value_domains (ctx:('a,'b,'c,'d) Analyses.ctx) t tcons =
match t.d with
| None -> []
| Some d ->
let constyp = Tcons1.get_typ tcons in
let match_compa = if constyp = SUP then Gt else Ge in
let mkconstraint coff index cst =
let varifo = Option.get @@ V.to_cil_varinfo (Environment.var_of_dim t.env index) in
let ik = Cilfacade.get_ikind (varifo.vtype) in
let lval = Lval (Var varifo, NoOffset) in
let monom = BinOp (Mult,Cil.kintegerCilint ik coff,lval,TInt (ik,[])) in
BinOp (match_compa, monom ,Cil.kintegerCilint ik cst,Cil.intType)
in
match simplified_monomials_from_texp t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with
| None -> []
| Some (sum_of_terms, (constant,divisor)) ->(
match sum_of_terms,constyp with
| [(coeff, index, divi)], SUP
| [(coeff, index, divi)], SUPEQ ->

let coeff = Z.(mul coeff divisor) in
let constant = Z.(mul constant divi) in
(* let constant = Z.(if constyp = SUP then sub constant one else constant) in
let constyp = Lincons0.SUPEQ in *)

(* now we have coeff*var_index + constant SUP(EQ) 0 *)
let opstr =Lincons0.string_of_typ constyp in
M.trace "refinevalues" "Refining due to %d: %s*%s + %s %s 0" index (Z.to_string (coeff)) (Var.to_string @@ Environment.var_of_dim t.env index) (Z.to_string (constant)) opstr;

(* make var_index = var_index explicit in conlist *)
let conlist = (index,(Some(Z.one,index),Z.zero,Z.one))::EConj.get_equivalent_expressions d index in
let conlist = List.map (fun (var,(rhsrep,rhsoffs,rhsdivi)) ->
let rhscoeff = BatOption.map_default (fst) Z.zero rhsrep in
let zsign z = Z.sign z |> Z.of_int in
let inverse vv tt=
let xx,yy,zz = snd @@ EqualitiesConjunction.inverse vv tt in
BatOption.map_default (fst) Z.zero xx ,yy,zz
in

(* coeff*var_index + constant SUP(EQ) 0 *)
(* and rhsdivi*var_var - rhsoffs = rhscoeff *var_index*)
(* establish the 2vareq for var_index: *)
let (newcoeff,newoffs,newdivi) = inverse 0(*var_var*) (rhscoeff,0(*var_index*),rhsoffs,rhsdivi) in
if M.tracing then M.trace "refinevalues" "2linvareq: var_%s = (%s*var_%s + %s)/%s" (Var.to_string @@ Environment.var_of_dim t.env index) (Z.to_string newcoeff) (Var.to_string @@ Environment.var_of_dim t.env var) (Z.to_string newoffs) (Z.to_string newdivi);
(* multiply with var_index's old coefficient (i.e. substitute /var_index): *)
let (newcoeff,newoffs,newdivi) = Z.(coeff*newcoeff,coeff*newoffs,newdivi) in
(* and add the constant: *)
let (newcoeff,newoffs,newdivi) = Z.(newcoeff,newoffs+(constant*newdivi),newdivi) in
(* Now get rid of divisor, except for sign: *)
let (newcoeff,newoffs,newdivi) = Z.(newcoeff*abs newdivi,newoffs * abs newdivi,of_int (sign newdivi)) in
(* Do the division, respecting the sign *)
let (newcoeff,newoffs,newdivi) = Z.(zsign newcoeff,(div newoffs (abs newcoeff))+ zsign (rem newoffs (abs newcoeff)),newdivi) in
(* multiply with the divisor, which is only ever 1 or -1 anyway *)
let (newcoeff,newoffs,newdivi) = Z.(newdivi*newcoeff,newoffs*newdivi,one) in
if M.tracing then M.trace "refinevalues" "=> (%s*var_%s +%s) / %s %s 0" (Z.to_string newcoeff) (Var.to_string @@ Environment.var_of_dim t.env var) (Z.to_string newoffs) (Z.to_string newdivi) opstr;
mkconstraint newcoeff var (Z.neg newoffs)) conlist in
M.trace "refinevalues" "Resulting refining assertions %s" (String.concat " ; " (List.map (fun c -> Pretty.sprint ~width:80 (d_exp () c)) conlist));
conlist
| _ -> [] )

(** Assert a constraint expression.
The overflow is completely handled by the flag "no_ov",
Expand Down Expand Up @@ -729,10 +800,10 @@ struct
| _ -> bot_env (* all other results are violating the guard *)
end
| [(coeff, index, divi)] -> (* guard has a single reference variable only *)
if Tcons1.get_typ tcons = EQ then
meet_with_one_conj t index (Rhs.canonicalize (None, Z.neg @@ Z.(divi*constant),Z.(coeff*divisor)))
else
t (* only EQ is supported in equality based domains *)
begin match Tcons1.get_typ tcons with
| EQ -> meet_with_one_conj t index (Rhs.canonicalize (None, Z.neg @@ Z.(divi*constant),Z.(coeff*divisor)))
| _ -> t (* only EQ is supported in equality based domains *)
end
| [(c1,var1,d1); (c2,var2,d2)] -> (* two variables in relation needs a little sorting out *)
begin match Tcons1.get_typ tcons with
| EQ -> (* c1*var1/d1 + c2*var2/d2 +constant/divisor = 0*)
Expand All @@ -748,7 +819,6 @@ struct
| _ -> t (* For equalities of more then 2 vars we just return t *))

let meet_tcons ask t tcons original_expr no_ov =
if M.tracing then M.tracel "meet_tcons" "meet_tcons with expr: %a no_ov:%b" d_exp original_expr (Lazy.force no_ov);
meet_tcons ask t tcons original_expr no_ov

let meet_tcons t tcons expr = timing_wrap "meet_tcons" (meet_tcons t tcons) expr
Expand Down Expand Up @@ -809,6 +879,8 @@ struct

let cil_exp_of_lincons1 = Convert.cil_exp_of_lincons1

let tcons1_of_cil_exp = Convert.tcons1_of_cil_exp

let env t = t.env

type marshal = t
Expand All @@ -819,7 +891,7 @@ struct

end

module D2: RelationDomain.RD with type var = Var.t =
module D2 =
struct
module D = D
module ConvArg = struct
Expand Down
63 changes: 63 additions & 0 deletions tests/regression/77-lin2vareq/35-refinement.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//SKIP PARAM: --enable ana.int.interval --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none
// initial test for interval-refinement by lin2vareq

#include <goblint.h>

void main() {
int a;
int b = -7*a+3;
int c = -5*a+5;
int d = 13*a+11;
int e = a;

if (b < 5){ // a < 1

__goblint_check(1 == 1); //SUCCESS
__goblint_check(a > -1); //SUCCESS
__goblint_check(c < 7); //SUCCESS
__goblint_check(d > 8); //SUCCESS
__goblint_check(e > -1); //SUCCESS

if (a < 1){

__goblint_check(1 == 1); //SUCCESS

// for invariants that solely depend on the reference variable without a factor, we get:
// short node: 2linvar does not know this, we get this from the interval domain
__goblint_check(a == 0); //SUCCESS
__goblint_check(e == 0); //SUCCESS

// TODO: a==0 is not fed back to 2linvar,
// thus 2linvar can not infer abcde being constant

// for all the others, we can not infer their constant value
__goblint_check(b > -4); //SUCCESS
__goblint_check(c > 0); //SUCCESS
__goblint_check(d < 24); //SUCCESS

// in theory, if we knew about a being constant, we could infer the following:
__goblint_check(b == 3); //UNKNOWN
__goblint_check(c == 5); //UNKNOWN
__goblint_check(d ==11); //UNKNOWN

b=42;
}

a=69;
}
else {

__goblint_check(1 == 1); //SUCCESS
__goblint_check(a <=-1); //SUCCESS
__goblint_check(a <=-1); //SUCCESS
__goblint_check(c >= 7); //SUCCESS
__goblint_check(d <= 8); //SUCCESS
__goblint_check(e <=-1); //SUCCESS

a = 6;
}
//
//if (a<1) {
//// __goblint_assert(b<5); //SUCCESS
//}
}

0 comments on commit c078c2a

Please sign in to comment.